diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 0d11128..1648b5d 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -124,6 +124,10 @@ struct snd_pcm_ops {
 #define SNDRV_PCM_RATE_96000		(1<<10)		/* 96000Hz */
 #define SNDRV_PCM_RATE_176400		(1<<11)		/* 176400Hz */
 #define SNDRV_PCM_RATE_192000		(1<<12)		/* 192000Hz */
+#define SNDRV_PCM_RATE_352800		(1<<13)		/* 352800Hz */
+#define SNDRV_PCM_RATE_384000		(1<<14)		/* 384000Hz */
+#define SNDRV_PCM_RATE_705600		(1<<15)		/* 705600Hz */
+#define SNDRV_PCM_RATE_768000		(1<<16)		/* 768000Hz */
 
 #define SNDRV_PCM_RATE_CONTINUOUS	(1<<30)		/* continuous range */
 #define SNDRV_PCM_RATE_KNOT		(1<<31)		/* supports more non-continuos rates */
@@ -136,6 +140,10 @@ struct snd_pcm_ops {
 					 SNDRV_PCM_RATE_88200|SNDRV_PCM_RATE_96000)
 #define SNDRV_PCM_RATE_8000_192000	(SNDRV_PCM_RATE_8000_96000|SNDRV_PCM_RATE_176400|\
 					 SNDRV_PCM_RATE_192000)
+#define SNDRV_PCM_RATE_8000_384000	(SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_352800|\
+					 SNDRV_PCM_RATE_384000)
+#define SNDRV_PCM_RATE_8000_768000	(SNDRV_PCM_RATE_8000_384000|SNDRV_PCM_RATE_705600|\
+					 SNDRV_PCM_RATE_768000)
 #define _SNDRV_PCM_FMTBIT(fmt)		(1ULL << (__force int)SNDRV_PCM_FORMAT_##fmt)
 #define SNDRV_PCM_FMTBIT_S8		_SNDRV_PCM_FMTBIT(S8)
 #define SNDRV_PCM_FMTBIT_U8		_SNDRV_PCM_FMTBIT(U8)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index d776291..167cbd2
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -1763,12 +1763,13 @@ static int snd_pcm_hw_rule_sample_bits(struct snd_pcm_hw_params *params,
 	return snd_interval_refine(hw_param_interval(params, rule->var), &t);
 }
 
-#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_192000 != 1 << 12
+#if SNDRV_PCM_RATE_5512 != 1 << 0 || SNDRV_PCM_RATE_768000 != 1 << 16
 #error "Change this table"
 #endif
 
 static unsigned int rates[] = { 5512, 8000, 11025, 16000, 22050, 32000, 44100,
-                                 48000, 64000, 88200, 96000, 176400, 192000 };
+                                 48000, 64000, 88200, 96000, 176400, 192000,
+								 352800, 384000, 705600, 768000 };
 
 const struct snd_pcm_hw_constraint_list snd_pcm_known_rates = {
 	.count = ARRAY_SIZE(rates),
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index adafead..2334cab 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -60,7 +60,7 @@ source "sound/soc/sunxi/Kconfig"
 source "sound/soc/sunxi/hdmiaudio/Kconfig"
 source "sound/soc/sunxi/spdif/Kconfig"
 # i2s needs various adjustments for sun7i
-if ARCH_SUN4I || ARCH_SUN5I
+if ARCH_SUN4I || ARCH_SUN5I || ARCH_SUN7I
 source "sound/soc/sunxi/i2s/Kconfig"
 endif
 endif
diff --git a/sound/soc/sunxi/hdmiaudio/sndhdmi.c b/sound/soc/sunxi/hdmiaudio/sndhdmi.c
index 1c306d2..f291c4b 100644
--- a/sound/soc/sunxi/hdmiaudio/sndhdmi.c
+++ b/sound/soc/sunxi/hdmiaudio/sndhdmi.c
@@ -22,12 +22,9 @@
 #include <sound/initval.h>
 #include <plat/sys_config.h>
 #include <linux/io.h>
-#include <linux/drv_hdmi.h>
 
-static __audio_hdmi_func	g_hdmi_func;
-static hdmi_audio_t		hdmi_para;
+#include "sndhdmi.h"
 
-/* The struct is just for registering the hdmiaudio codec node */
 struct sndhdmi_priv {
 	int sysclk;
 	int dai_fmt;
@@ -36,11 +33,15 @@ struct sndhdmi_priv {
 	struct snd_pcm_substream *slave_substream;
 };
 
-void audio_set_hdmi_func(__audio_hdmi_func *hdmi_func)
+static hdmi_audio_t hdmi_para;
+static __audio_hdmi_func g_hdmi_func;
+
+void audio_set_hdmi_func(__audio_hdmi_func * hdmi_func)
 {
 	g_hdmi_func.hdmi_audio_enable = hdmi_func->hdmi_audio_enable;
 	g_hdmi_func.hdmi_set_audio_para = hdmi_func->hdmi_set_audio_para;
 }
+
 EXPORT_SYMBOL(audio_set_hdmi_func);
 
 #define SNDHDMI_RATES  (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT)
@@ -53,25 +54,21 @@ static int sndhdmi_mute(struct snd_soc_dai *dai, int mute)
 }
 
 static int sndhdmi_startup(struct snd_pcm_substream *substream,
-						struct snd_soc_dai *dai)
+	struct snd_soc_dai *dai)
 {
 	return 0;
 }
 
 static void sndhdmi_shutdown(struct snd_pcm_substream *substream,
-						struct snd_soc_dai *dai)
+	struct snd_soc_dai *dai)
 {
 
 }
 
 static int sndhdmi_hw_params(struct snd_pcm_substream *substream,
-				struct snd_pcm_hw_params *params,
-				struct snd_soc_dai *dai)
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
 {
-	if ((!substream) || (!params)) {
-		printk("error:%s,line:%d\n", __func__, __LINE__);
-		return -EAGAIN;
-	}
 	hdmi_para.sample_rate = params_rate(params);
 	hdmi_para.channel_num = params_channels(params);
 	switch (params_format(params)) {
@@ -94,14 +91,13 @@ static int sndhdmi_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int sndhdmi_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
-						unsigned int freq, int dir)
+static int sndhdmi_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+				  int clk_id, unsigned int freq, int dir)
 {
 	return 0;
 }
 
-static int sndhdmi_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id,
-									int div)
+static int sndhdmi_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
 {
 
 	hdmi_para.fs_between = div;
@@ -110,12 +106,13 @@ static int sndhdmi_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id,
 }
 
 
-static int sndhdmi_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+static int sndhdmi_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			       unsigned int fmt)
 {
 	return 0;
 }
 
-/* codec dai operation */
+//codec dai operation
 struct snd_soc_dai_ops sndhdmi_dai_ops = {
 	.startup = sndhdmi_startup,
 	.shutdown = sndhdmi_shutdown,
@@ -126,7 +123,7 @@ struct snd_soc_dai_ops sndhdmi_dai_ops = {
 	.set_fmt = sndhdmi_set_dai_fmt,
 };
 
-/* codec dai */
+//codec dai
 struct snd_soc_dai_driver sndhdmi_dai = {
 	.name = "sndhdmi",
 	/* playback capabilities */
@@ -147,17 +144,11 @@ static int sndhdmi_soc_probe(struct snd_soc_codec *codec)
 {
 	struct sndhdmi_priv *sndhdmi;
 
-	if (!codec) {
-		printk("error:%s,line:%d\n", __func__, __LINE__);
-		return -EAGAIN;
-	}
-
 	sndhdmi = kzalloc(sizeof(struct sndhdmi_priv), GFP_KERNEL);
-	if (sndhdmi == NULL) {
-		printk("error at:%s,%d\n", __func__, __LINE__);
+	if(sndhdmi == NULL){
+		printk("error at:%s,%d\n",__func__,__LINE__);
 		return -ENOMEM;
 	}
-
 	snd_soc_codec_set_drvdata(codec, sndhdmi);
 
 	return 0;
@@ -165,13 +156,7 @@ static int sndhdmi_soc_probe(struct snd_soc_codec *codec)
 
 static int sndhdmi_soc_remove(struct snd_soc_codec *codec)
 {
-	struct sndhdmi_priv *sndhdmi;
-
-	if (!codec) {
-		printk("error:%s,line:%d\n", __func__, __LINE__);
-		return -EAGAIN;
-	}
-	sndhdmi = snd_soc_codec_get_drvdata(codec);
+	struct sndhdmi_priv *sndhdmi = snd_soc_codec_get_drvdata(codec);
 
 	kfree(sndhdmi);
 
@@ -179,26 +164,17 @@ static int sndhdmi_soc_remove(struct snd_soc_codec *codec)
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_sndhdmi = {
-	.probe		= sndhdmi_soc_probe,
-	.remove		= sndhdmi_soc_remove,
+	.probe =        sndhdmi_soc_probe,
+	.remove =       sndhdmi_soc_remove,
 };
 
 static int __devinit sndhdmi_codec_probe(struct platform_device *pdev)
 {
-	if (!pdev) {
-		printk("error:%s,line:%d\n", __func__, __LINE__);
-		return -EAGAIN;
-	}
-	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sndhdmi,
-							&sndhdmi_dai, 1);
+	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sndhdmi, &sndhdmi_dai, 1);
 }
 
-static int __exit sndhdmi_codec_remove(struct platform_device *pdev)
+static int __devexit sndhdmi_codec_remove(struct platform_device *pdev)
 {
-	if (!pdev) {
-		printk("error:%s,line:%d\n", __func__, __LINE__);
-		return -EAGAIN;
-	}
 	snd_soc_unregister_codec(&pdev->dev);
 	return 0;
 }
@@ -209,15 +185,14 @@ static struct platform_driver sndhdmi_codec_driver = {
 		.owner = THIS_MODULE,
 	},
 	.probe = sndhdmi_codec_probe,
-	.remove = __exit_p(sndhdmi_codec_remove),
+	.remove = __devexit_p(sndhdmi_codec_remove),
 };
 
 static int __init sndhdmi_codec_init(void)
 {
 	int err = 0;
 
-	err = platform_driver_register(&sndhdmi_codec_driver);
-	if (err < 0)
+	if ((err = platform_driver_register(&sndhdmi_codec_driver)) < 0)
 		return err;
 
 	return 0;
@@ -233,3 +208,4 @@ module_exit(sndhdmi_codec_exit);
 MODULE_DESCRIPTION("SNDHDMI ALSA soc codec driver");
 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
 MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/sunxi/hdmiaudio/sndhdmi.h b/sound/soc/sunxi/hdmiaudio/sndhdmi.h
new file mode 100644
index 0000000..346e587
--- /dev/null
+++ b/sound/soc/sunxi/hdmiaudio/sndhdmi.h
@@ -0,0 +1,36 @@
+/*
+ * sound\soc\sunxi\hdmiaudio\sndhdmi.h
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * chenpailin <chenpailin@allwinnertech.com>
+ *
+ * some simple description for this code
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ */
+#ifndef SNDHDMI_H
+#define SNDHDMI_H
+#include <linux/drv_hdmi.h>
+
+struct sndhdmi_platform_data {
+	void (*power) (int);
+	int model;
+	/*
+	  ALSA SOC usually puts the device in standby mode when it's not used
+	  for sometime. If you unset is_powered_on_standby the driver will
+	  turn off the ADC/DAC when this callback is invoked and turn it back
+	  on when needed. Unfortunately this will result in a very light bump
+	  (it can be audible only with good earphones). If this bothers you
+	  set is_powered_on_standby, you will have slightly higher power
+	  consumption. Please note that sending the L3 command for ADC is
+	  enough to make the bump, so it doesn't make difference if you
+	  completely take off power from the codec.
+	*/
+	int is_powered_on_standby;
+};
+
+#endif
diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.c b/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.c
index 12da1a6..5080f10 100644
--- a/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.c
+++ b/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.c
@@ -34,43 +34,300 @@
 #include <plat/sys_config.h>
 #include <plat/dma_compat.h>
 
+#include "sunxi-hdmipcm.h"
+#include "sunxi-hdmiaudio.h"
+
+//save the register value
+static int regsave[8];
+
 static struct sunxi_dma_params sunxi_hdmiaudio_pcm_stereo_out = {
 	.client.name	=	"HDMIAUDIO PCM Stereo out",
 #if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I
 	.channel	=	DMACH_HDMIAUDIO,
 #endif
-	.dma_addr	=	0,
+	.dma_addr 	=	0,
+};
+
+static struct sunxi_dma_params sunxi_hdmiaudio_pcm_stereo_in = {
+	.client.name	=	"HDMIAUDIO PCM Stereo in",
+#if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I
+	.channel	=	DMACH_HDMIAUDIO,
+#endif
+	.dma_addr 	=	SUNXI_HDMIAUDIOBASE + SUNXI_HDMIAUDIORXFIFO,
 };
 
+struct sunxi_hdmiaudio_info sunxi_hdmiaudio;
+
+//clock handle
+static struct clk *hdmiaudio_apbclk;
+static struct clk *hdmiaudio_pll2clk;
+static struct clk *hdmiaudio_pllx8;
+static struct clk *hdmiaudio_moduleclk;
+
 void sunxi_snd_txctrl_hdmiaudio(struct snd_pcm_substream *substream, int on)
 {
+	u32 reg_val;
+
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_TXCHSEL);
+	reg_val &= ~0x7;
+	reg_val |= SUNXI_TXCHSEL_CHNUM(substream->runtime->channels);
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_TXCHSEL);
+
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_TXCHMAP);
+	reg_val = 0;
+	if(substream->runtime->channels == 1) {
+		reg_val = 0x76543200;
+	} else {
+		reg_val = 0x76543210;
+	}
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_TXCHMAP);
+
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	reg_val &= ~SUNXI_HDMIAUDIOCTL_SDO3EN;
+	reg_val &= ~SUNXI_HDMIAUDIOCTL_SDO2EN;
+	reg_val &= ~SUNXI_HDMIAUDIOCTL_SDO1EN;
+	reg_val &= ~SUNXI_HDMIAUDIOCTL_SDO0EN;
+	switch(substream->runtime->channels) {
+		case 1:
+		case 2:
+			reg_val |= SUNXI_HDMIAUDIOCTL_SDO0EN; break;
+		case 3:
+		case 4:
+			reg_val |= SUNXI_HDMIAUDIOCTL_SDO0EN | SUNXI_HDMIAUDIOCTL_SDO1EN; break;
+		case 5:
+		case 6:
+			reg_val |= SUNXI_HDMIAUDIOCTL_SDO0EN | SUNXI_HDMIAUDIOCTL_SDO1EN | SUNXI_HDMIAUDIOCTL_SDO2EN; break;
+		case 7:
+		case 8:
+			reg_val |= SUNXI_HDMIAUDIOCTL_SDO0EN | SUNXI_HDMIAUDIOCTL_SDO1EN | SUNXI_HDMIAUDIOCTL_SDO2EN | SUNXI_HDMIAUDIOCTL_SDO3EN; break;
+		default:
+			reg_val |= SUNXI_HDMIAUDIOCTL_SDO0EN; break;
+	}
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+
+	//flush TX FIFO
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL);
+	reg_val |= SUNXI_HDMIAUDIOFCTL_FTX;
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL);
+
+	//clear TX counter
+	writel(0, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOTXCNT);
+
+	if (on) {
+		/* hdmiaudio TX ENABLE */
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+		reg_val |= SUNXI_HDMIAUDIOCTL_TXEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+
+		/* enable DMA DRQ mode for play */
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT);
+		reg_val |= SUNXI_HDMIAUDIOINT_TXDRQEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT);
+
+		//Global Enable Digital Audio Interface
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+		reg_val |= SUNXI_HDMIAUDIOCTL_GEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	}else{
+		/* HDMIAUDIO TX DISABLE */
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+		reg_val &= ~SUNXI_HDMIAUDIOCTL_TXEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+
+		/* DISBALE dma DRQ mode */
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT);
+		reg_val &= ~SUNXI_HDMIAUDIOINT_TXDRQEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT);
+
+		//Global disable Digital Audio Interface
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+		reg_val &= ~SUNXI_HDMIAUDIOCTL_GEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	}
 }
 
-static int sunxi_hdmiaudio_set_fmt(struct snd_soc_dai *cpu_dai,
-							unsigned int fmt)
+void sunxi_snd_rxctrl_hdmiaudio(struct snd_pcm_substream *substream, int on)
 {
-	return 0;
+	u32 reg_val;
+
+	//flush RX FIFO
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL);
+	reg_val |= SUNXI_HDMIAUDIOFCTL_FRX;
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL);
+
+	//clear RX counter
+	writel(0, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIORXCNT);
+
+	if (on) {
+		/* HDMIAUDIO RX ENABLE */
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+		reg_val |= SUNXI_HDMIAUDIOCTL_RXEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+
+		/* enable DMA DRQ mode for record */
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT);
+		reg_val |= SUNXI_HDMIAUDIOINT_RXDRQEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT);
+
+		//Global Enable Digital Audio Interface
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+		reg_val |= SUNXI_HDMIAUDIOCTL_GEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	} else {
+		/* HDMIAUDIO RX DISABLE */
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+		reg_val &= ~SUNXI_HDMIAUDIOCTL_RXEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+
+		/* DISBALE dma DRQ mode */
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT);
+		reg_val &= ~SUNXI_HDMIAUDIOINT_RXDRQEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT);
+
+		//Global disable Digital Audio Interface
+		reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+		reg_val &= ~SUNXI_HDMIAUDIOCTL_GEN;
+		writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	}
 }
 
-static int sunxi_hdmiaudio_hw_params(struct snd_pcm_substream *substream,
-					struct snd_pcm_hw_params *params,
-					struct snd_soc_dai *dai)
+static int sunxi_hdmiaudio_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 {
-	struct snd_soc_pcm_runtime *rtd;
-	struct sunxi_dma_params *dma_data;
+	u32 reg_val;
+	u32 reg_val1;
 
-	if (!substream) {
-		printk("error:%s,line:%d\n", __func__, __LINE__);
-		return -EAGAIN;
+	if (sunxi_is_sun7i())
+		return 0; /* No rx / tx control, etc. on sun7i() */
+
+	//SDO ON
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	reg_val |= (SUNXI_HDMIAUDIOCTL_SDO0EN | SUNXI_HDMIAUDIOCTL_SDO1EN | SUNXI_HDMIAUDIOCTL_SDO2EN | SUNXI_HDMIAUDIOCTL_SDO3EN);
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+
+	/* master or slave selection */
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+		case SND_SOC_DAIFMT_CBM_CFM:   /* codec clk & frm master */
+			reg_val |= SUNXI_HDMIAUDIOCTL_MS;
+			break;
+		case SND_SOC_DAIFMT_CBS_CFS:   /* codec clk & frm slave */
+			reg_val &= ~SUNXI_HDMIAUDIOCTL_MS;
+			break;
+		default:
+			return -EINVAL;
+	}
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+
+	/* pcm or hdmiaudio mode selection */
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	reg_val1 = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0);
+	reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_FMT_RVD;
+	switch(fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+		case SND_SOC_DAIFMT_I2S:        /* I2S mode */
+			reg_val &= ~SUNXI_HDMIAUDIOCTL_PCM;
+			reg_val1 |= SUNXI_HDMIAUDIOFAT0_FMT_I2S;
+			break;
+		case SND_SOC_DAIFMT_RIGHT_J:    /* Right Justified mode */
+			reg_val &= ~SUNXI_HDMIAUDIOCTL_PCM;
+			reg_val1 |= SUNXI_HDMIAUDIOFAT0_FMT_RGT;
+			break;
+		case SND_SOC_DAIFMT_LEFT_J:     /* Left Justified mode */
+			reg_val &= ~SUNXI_HDMIAUDIOCTL_PCM;
+			reg_val1 |= SUNXI_HDMIAUDIOFAT0_FMT_LFT;
+			break;
+		case SND_SOC_DAIFMT_DSP_A:      /* L data msb after FRM LRC */
+			reg_val |= SUNXI_HDMIAUDIOCTL_PCM;
+			reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_LRCP;
+			break;
+		case SND_SOC_DAIFMT_DSP_B:      /* L data msb during FRM LRC */
+			reg_val |= SUNXI_HDMIAUDIOCTL_PCM;
+			reg_val1 |= SUNXI_HDMIAUDIOFAT0_LRCP;
+			break;
+		default:
+			return -EINVAL;
 	}
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	writel(reg_val1, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0);
+
+	/* DAI signal inversions */
+	reg_val1 = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0);
+	switch(fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:     /* normal bit clock + frame */
+			reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_LRCP;
+			reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_BCP;
+			break;
+		case SND_SOC_DAIFMT_NB_IF:     /* normal bclk + inv frm */
+			reg_val1 |= SUNXI_HDMIAUDIOFAT0_LRCP;
+			reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_BCP;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:     /* invert bclk + nor frm */
+			reg_val1 &= ~SUNXI_HDMIAUDIOFAT0_LRCP;
+			reg_val1 |= SUNXI_HDMIAUDIOFAT0_BCP;
+			break;
+		case SND_SOC_DAIFMT_IB_IF:     /* invert bclk + frm */
+			reg_val1 |= SUNXI_HDMIAUDIOFAT0_LRCP;
+			reg_val1 |= SUNXI_HDMIAUDIOFAT0_BCP;
+			break;
+	}
+	writel(reg_val1, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0);
+
+	/* word select size */
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0);
+	reg_val &= ~SUNXI_HDMIAUDIOFAT0_WSS_32BCLK;
+	if(sunxi_hdmiaudio.ws_size == 16)
+		reg_val |= SUNXI_HDMIAUDIOFAT0_WSS_16BCLK;
+	else if(sunxi_hdmiaudio.ws_size == 20)
+		reg_val |= SUNXI_HDMIAUDIOFAT0_WSS_20BCLK;
+	else if(sunxi_hdmiaudio.ws_size == 24)
+		reg_val |= SUNXI_HDMIAUDIOFAT0_WSS_24BCLK;
+	else
+		reg_val |= SUNXI_HDMIAUDIOFAT0_WSS_32BCLK;
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0);
+
+	/* PCM REGISTER setup */
+	reg_val = sunxi_hdmiaudio.pcm_txtype&0x3;
+	reg_val |= sunxi_hdmiaudio.pcm_rxtype<<2;
+
+	if(!sunxi_hdmiaudio.pcm_sync_type)
+		reg_val |= SUNXI_HDMIAUDIOFAT1_SSYNC;							//short sync
+	if(sunxi_hdmiaudio.pcm_sw == 16)
+		reg_val |= SUNXI_HDMIAUDIOFAT1_SW;
+
+	reg_val |=((sunxi_hdmiaudio.pcm_start_slot - 1)&0x3)<<6;		//start slot index
+
+	reg_val |= sunxi_hdmiaudio.pcm_lsb_first<<9;			//MSB or LSB first
+
+	if(sunxi_hdmiaudio.pcm_sync_period == 256)
+		reg_val |= 0x4<<12;
+	else if (sunxi_hdmiaudio.pcm_sync_period == 128)
+		reg_val |= 0x3<<12;
+	else if (sunxi_hdmiaudio.pcm_sync_period == 64)
+		reg_val |= 0x2<<12;
+	else if (sunxi_hdmiaudio.pcm_sync_period == 32)
+		reg_val |= 0x1<<12;
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT1);
+
+	/* set FIFO control register */
+	reg_val = 0 & 0x3;
+	reg_val |= (0 & 0x1)<<2;
+	reg_val |= SUNXI_HDMIAUDIOFCTL_RXTL(0xf);				//RX FIFO trigger level
+	reg_val |= SUNXI_HDMIAUDIOFCTL_TXTL(0x40);				//TX FIFO empty trigger level
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL);
+	return 0;
+}
 
-	rtd = substream->private_data;
+static int sunxi_hdmiaudio_hw_params(struct snd_pcm_substream *substream,
+																struct snd_pcm_hw_params *params,
+																struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct sunxi_dma_params *dma_data;
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+	/* play or record */
+	if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		dma_data = &sunxi_hdmiaudio_pcm_stereo_out;
 	else
-		printk("error:hdmiaudio can't support capture:%s,line:%d\n",
-							__func__, __LINE__);
+		dma_data = &sunxi_hdmiaudio_pcm_stereo_in;
 
 	snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
 
@@ -78,7 +335,7 @@ static int sunxi_hdmiaudio_hw_params(struct snd_pcm_substream *substream,
 }
 
 static int sunxi_hdmiaudio_trigger(struct snd_pcm_substream *substream,
-					int cmd, struct snd_soc_dai *dai)
+                              int cmd, struct snd_soc_dai *dai)
 {
 	int ret = 0;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -89,34 +346,102 @@ static int sunxi_hdmiaudio_trigger(struct snd_pcm_substream *substream,
 		return 0; /* No rx / tx control, etc. on sun7i() */
 
 	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		sunxi_snd_txctrl_hdmiaudio(substream, 1);
-		sunxi_dma_started(dma_data);
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		sunxi_snd_txctrl_hdmiaudio(substream, 0);
-		break;
-	default:
-		ret = -EINVAL;
-		break;
+		case SNDRV_PCM_TRIGGER_START:
+		case SNDRV_PCM_TRIGGER_RESUME:
+		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+			if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+				sunxi_snd_rxctrl_hdmiaudio(substream, 1);
+			} else {
+				sunxi_snd_txctrl_hdmiaudio(substream, 1);
+			}
+			sunxi_dma_started(dma_data);
+			break;
+		case SNDRV_PCM_TRIGGER_STOP:
+		case SNDRV_PCM_TRIGGER_SUSPEND:
+		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+			if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+				sunxi_snd_rxctrl_hdmiaudio(substream, 0);
+			} else {
+			  sunxi_snd_txctrl_hdmiaudio(substream, 0);
+			}
+			break;
+		default:
+			ret = -EINVAL;
+			break;
 	}
 	return ret;
 }
 
-/* freq: 1: 22.5792MHz 0: 24.576MHz */
+//freq:   1: 22.5792MHz   0: 24.576MHz
 static int sunxi_hdmiaudio_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
-						unsigned int freq, int dir)
+                                 unsigned int freq, int dir)
 {
+	if (sunxi_is_sun7i())
+		return 0; /* No rx / tx control, etc. on sun7i() */
+
+	if (!freq) {
+		clk_set_rate(hdmiaudio_pll2clk, 24576000);
+	} else {
+		clk_set_rate(hdmiaudio_pll2clk, 22579200);
+	}
+
 	return 0;
 }
 
-static int sunxi_hdmiaudio_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id,
-									int div)
+static int sunxi_hdmiaudio_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div)
 {
+	u32 reg;
+
+	if (sunxi_is_sun7i())
+		return 0; /* No rx / tx control, etc. on sun7i() */
+
+	switch (div_id) {
+	case SUNXI_DIV_MCLK:
+		if(div <= 8)
+			div  = (div >>1);
+		else if(div  == 12)
+			div  = 0x5;
+		else if(div  == 16)
+			div  = 0x6;
+		else if(div == 24)
+			div = 0x7;
+		else if(div == 32)
+			div = 0x8;
+		else if(div == 48)
+			div = 0x9;
+		else if(div == 64)
+			div = 0xa;
+		reg = (readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD) & ~SUNXI_HDMIAUDIOCLKD_MCLK_MASK) | (div << SUNXI_HDMIAUDIOCLKD_MCLK_OFFS);
+		writel(reg, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD);
+		break;
+	case SUNXI_DIV_BCLK:
+		if(div <= 8)
+			div = (div>>1) - 1;
+		else if(div == 12)
+			div = 0x4;
+		else if(div == 16)
+			div = 0x5;
+		else if(div == 32)
+			div = 0x6;
+		else if(div == 64)
+			div = 0x7;
+		reg = (readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD) & ~SUNXI_HDMIAUDIOCLKD_BCLK_MASK) | (div <<SUNXI_HDMIAUDIOCLKD_BCLK_OFFS);
+		writel(reg, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	//diable MCLK output when high samplerate
+	reg = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD);
+	if(!(reg & 0xF)) {
+		reg &= ~SUNXI_HDMIAUDIOCLKD_MCLKOEN;
+		writel(reg, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD);
+	} else {
+		reg |= SUNXI_HDMIAUDIOCLKD_MCLKOEN;
+		writel(reg, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD);
+	}
+
 	return 0;
 }
 
@@ -135,78 +460,205 @@ static int sunxi_hdmiaudio_dai_remove(struct snd_soc_dai *dai)
 	return 0;
 }
 
+static void hdmiaudioregsave(void)
+{
+	regsave[0] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	regsave[1] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0);
+	regsave[2] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT1);
+	regsave[3] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL) | (0x3<<24);
+	regsave[4] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT);
+	regsave[5] = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD);
+	regsave[6] = readl(sunxi_hdmiaudio.regs + SUNXI_TXCHSEL);
+	regsave[7] = readl(sunxi_hdmiaudio.regs + SUNXI_TXCHMAP);
+}
+
+static void hdmiaudioregrestore(void)
+{
+	writel(regsave[0], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	writel(regsave[1], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT0);
+	writel(regsave[2], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFAT1);
+	writel(regsave[3], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOFCTL);
+	writel(regsave[4], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOINT);
+	writel(regsave[5], sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCLKD);
+	writel(regsave[6], sunxi_hdmiaudio.regs + SUNXI_TXCHSEL);
+	writel(regsave[7], sunxi_hdmiaudio.regs + SUNXI_TXCHMAP);
+}
+
 static int sunxi_hdmiaudio_suspend(struct snd_soc_dai *cpu_dai)
 {
-	printk("[HDMIAUDIO]Entered %s\n", __func__);
+	u32 reg_val;
+
+	if (sunxi_is_sun7i())
+		return 0; /* No rx / tx control, etc. on sun7i() */
 
-	/*
-	 *	printk("[HDMIAUDIO]PLL2 0x01c20008 = %#x, line = %d\n",
-	 *			*(volatile int *)0xF1C20008, __LINE__);
-	 */
-	printk("[HDMIAUDIO]SPECIAL CLK 0x01c20068 = %#x, line= %d\n",
-					*(volatile int *)0xF1C20068, __LINE__);
-	printk("[HDMIAUDIO]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n",
-					*(volatile int *)0xF1C200B8, __LINE__);
+ 	printk("[HDMIAUDIO]Entered %s\n", __func__);
+
+	//Global Enable Digital Audio Interface
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	reg_val &= ~SUNXI_HDMIAUDIOCTL_GEN;
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+
+	hdmiaudioregsave();
+	//release the module clock
+	clk_disable(hdmiaudio_moduleclk);
+
+	clk_disable(hdmiaudio_apbclk);
+
+	//printk("[HDMIAUDIO]PLL2 0x01c20008 = %#x, line = %d\n", *(volatile int*)0xF1C20008, __LINE__);
+	printk("[HDMIAUDIO]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__);
+	printk("[HDMIAUDIO]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__);
 
 	return 0;
 }
 
 static int sunxi_hdmiaudio_resume(struct snd_soc_dai *cpu_dai)
 {
+	u32 reg_val;
+
+	if (sunxi_is_sun7i())
+		return 0; /* No rx / tx control, etc. on sun7i() */
+
 	printk("[HDMIAUDIO]Entered %s\n", __func__);
 
-	/*
-	 *	printk("[HDMIAUDIO]PLL2 0x01c20008 = %#x, line = %d\n",
-	 *				*(volatile int *)0xF1C20008, __LINE__);
-	 */
-	printk("[HDMIAUDIO]SPECIAL CLK 0x01c20068 = %#x, line= %d\n",
-					*(volatile int *)0xF1C20068, __LINE__);
-	printk("[HDMIAUDIO]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n",
-					*(volatile int *)0xF1C200B8, __LINE__);
+	//release the module clock
+	clk_enable(hdmiaudio_apbclk);
+
+	//release the module clock
+	clk_enable(hdmiaudio_moduleclk);
+
+	hdmiaudioregrestore();
+
+	//Global Enable Digital Audio Interface
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	reg_val |= SUNXI_HDMIAUDIOCTL_GEN;
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+
+	//printk("[HDMIAUDIO]PLL2 0x01c20008 = %#x, line = %d\n", *(volatile int*)0xF1C20008, __LINE__);
+	printk("[HDMIAUDIO]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__);
+	printk("[HDMIAUDIO]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__);
 
 	return 0;
 }
 
-#define SUNXI_HDMI_RATES (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT)
+#define SUNXI_I2S_RATES (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT)
 static struct snd_soc_dai_ops sunxi_hdmiaudio_dai_ops = {
-	.trigger		= sunxi_hdmiaudio_trigger,
-	.hw_params		= sunxi_hdmiaudio_hw_params,
-	.set_fmt		= sunxi_hdmiaudio_set_fmt,
-	.set_clkdiv		= sunxi_hdmiaudio_set_clkdiv,
-	.set_sysclk		= sunxi_hdmiaudio_set_sysclk,
+	.trigger 		= sunxi_hdmiaudio_trigger,
+	.hw_params 	= sunxi_hdmiaudio_hw_params,
+	.set_fmt 		= sunxi_hdmiaudio_set_fmt,
+	.set_clkdiv = sunxi_hdmiaudio_set_clkdiv,
+	.set_sysclk = sunxi_hdmiaudio_set_sysclk,
 };
 static struct snd_soc_dai_driver sunxi_hdmiaudio_dai = {
-
-	.probe			= sunxi_hdmiaudio_dai_probe,
-	.suspend		= sunxi_hdmiaudio_suspend,
-	.resume			= sunxi_hdmiaudio_resume,
-	.remove			= sunxi_hdmiaudio_dai_remove,
-	.playback		= {
-		.channels_min	= 1,
-		.channels_max	= 2,
-		.rates		= SUNXI_HDMI_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.probe 		= sunxi_hdmiaudio_dai_probe,
+	.suspend 	= sunxi_hdmiaudio_suspend,
+	.resume 	= sunxi_hdmiaudio_resume,
+	.remove 	= sunxi_hdmiaudio_dai_remove,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SUNXI_I2S_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
 	},
-	.symmetric_rates	= 1,
-	.ops			= &sunxi_hdmiaudio_dai_ops,
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SUNXI_I2S_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE
+	},
+	.symmetric_rates = 1,
+	.ops = &sunxi_hdmiaudio_dai_ops,
 };
 
 static int __devinit sunxi_hdmiaudio_dev_probe(struct platform_device *pdev)
 {
-	return snd_soc_register_dai(&pdev->dev, &sunxi_hdmiaudio_dai);
+	int reg_val = 0;
+	int ret = 0;
+
+	if (sunxi_is_sun7i()) {
+		/* No rx / tx control, etc. on sun7i() */
+		return snd_soc_register_dai(&pdev->dev, &sunxi_hdmiaudio_dai);
+	}
+
+	sunxi_hdmiaudio.regs = ioremap(SUNXI_HDMIAUDIOBASE, 0x100);
+	if (sunxi_hdmiaudio.regs == NULL)
+		return -ENXIO;
+
+	sunxi_hdmiaudio.ccmregs = ioremap(SUNXI_CCMBASE, 0x100);
+	if (sunxi_hdmiaudio.ccmregs == NULL)
+		return -ENXIO;
+
+	sunxi_hdmiaudio.ioregs = ioremap(0x01C20800, 0x100);
+	if (sunxi_hdmiaudio.ioregs == NULL)
+		return -ENXIO;
+
+	//hdmiaudio apbclk
+	hdmiaudio_apbclk = clk_get(NULL, "apb_i2s");
+	if(-1 == clk_enable(hdmiaudio_apbclk)){
+		printk("hdmiaudio_apbclk failed! line = %d\n", __LINE__);
+	}
+
+	hdmiaudio_pllx8 = clk_get(NULL, "audio_pllx8");
+
+	//hdmiaudio pll2clk
+	hdmiaudio_pll2clk = clk_get(NULL, "audio_pll");
+
+	//hdmiaudio module clk
+	hdmiaudio_moduleclk = clk_get(NULL, "i2s");
+
+	if(clk_set_parent(hdmiaudio_moduleclk, hdmiaudio_pll2clk)){
+		printk("try to set parent of hdmiaudio_moduleclk to hdmiaudio_pll2ck failed! line = %d\n",__LINE__);
+	}
+
+
+	if(clk_set_rate(hdmiaudio_moduleclk, 24576000/8)){
+		printk("set hdmiaudio_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__);
+	}
+
+
+	if(-1 == clk_enable(hdmiaudio_moduleclk)){
+		printk("open hdmiaudio_moduleclk failed! line = %d\n", __LINE__);
+	}
+
+	reg_val = readl(sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+	reg_val |= SUNXI_HDMIAUDIOCTL_GEN;
+	writel(reg_val, sunxi_hdmiaudio.regs + SUNXI_HDMIAUDIOCTL);
+
+	ret = snd_soc_register_dai(&pdev->dev, &sunxi_hdmiaudio_dai);
+
+	iounmap(sunxi_hdmiaudio.ioregs);
+
+	return 0;
 }
 
 static int __devexit sunxi_hdmiaudio_dev_remove(struct platform_device *pdev)
 {
+	if (sunxi_is_sun7i()) {
+		/* No rx / tx control, etc. on sun7i() */
+		snd_soc_unregister_dai(&pdev->dev);
+		return 0;
+	}
+
+	//release the module clock
+	clk_disable(hdmiaudio_moduleclk);
+
+	//release pllx8clk
+	clk_put(hdmiaudio_pllx8);
+
+	//release pll2clk
+	clk_put(hdmiaudio_pll2clk);
+
+	//release apbclk
+	clk_put(hdmiaudio_apbclk);
+
 	snd_soc_unregister_dai(&pdev->dev);
 	platform_set_drvdata(pdev, NULL);
 	return 0;
 }
 
 static struct platform_driver sunxi_hdmiaudio_driver = {
-	.probe	= sunxi_hdmiaudio_dev_probe,
-	.remove	= __devexit_p(sunxi_hdmiaudio_dev_remove),
-	.driver	= {
+	.probe = sunxi_hdmiaudio_dev_probe,
+	.remove = __devexit_p(sunxi_hdmiaudio_dev_remove),
+	.driver = {
 		.name = "sunxi-hdmiaudio",
 		.owner = THIS_MODULE,
 	},
@@ -216,8 +668,7 @@ static int __init sunxi_hdmiaudio_init(void)
 {
 	int err = 0;
 
-	err = platform_driver_register(&sunxi_hdmiaudio_driver);
-	if (err < 0)
+	if ((err = platform_driver_register(&sunxi_hdmiaudio_driver)) < 0)
 		return err;
 
 	return 0;
diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.h b/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.h
new file mode 100644
index 0000000..9f4a0cc
--- /dev/null
+++ b/sound/soc/sunxi/hdmiaudio/sunxi-hdmiaudio.h
@@ -0,0 +1,303 @@
+/*
+ * sound\soc\sunxi\hdmiaudio\sunxi-hdmiaudio.h
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * chenpailin <chenpailin@allwinnertech.com>
+ *
+ * some simple description for this code
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ */
+#ifndef SUNXI_HDMIAUIDO_H_
+#define SUNXI_HDMIAUIDO_H_
+#include <linux/drv_hdmi.h>
+
+/*------------------------------------------------------------*/
+/* REGISTER definition */
+
+/* HDMIAUDIO REGISTER */
+#define SUNXI_HDMIAUDIOBASE 		(0x01C22400)
+
+#define SUNXI_HDMIAUDIOCTL 	  (0x00)
+	#define SUNXI_HDMIAUDIOCTL_SDO3EN		(1<<11)
+	#define SUNXI_HDMIAUDIOCTL_SDO2EN		(1<<10)
+	#define SUNXI_HDMIAUDIOCTL_SDO1EN		(1<<9)
+	#define SUNXI_HDMIAUDIOCTL_SDO0EN		(1<<8)
+	#define SUNXI_HDMIAUDIOCTL_ASS			(1<<6)
+	#define SUNXI_HDMIAUDIOCTL_MS			(1<<5)
+	#define SUNXI_HDMIAUDIOCTL_PCM			(1<<4)
+	#define SUNXI_HDMIAUDIOCTL_LOOP			(1<<3)
+	#define SUNXI_HDMIAUDIOCTL_TXEN			(1<<2)
+	#define SUNXI_HDMIAUDIOCTL_RXEN			(1<<1)
+	#define SUNXI_HDMIAUDIOCTL_GEN			(1<<0)
+
+#define SUNXI_HDMIAUDIOFAT0 		(0x04)
+	#define SUNXI_HDMIAUDIOFAT0_LRCP					(1<<7)
+	#define SUNXI_HDMIAUDIOFAT0_BCP					(1<<6)
+	#define SUNXI_HDMIAUDIOFAT0_SR_RVD				(3<<4)
+	#define SUNXI_HDMIAUDIOFAT0_SR_16BIT				(0<<4)
+	#define	SUNXI_HDMIAUDIOFAT0_SR_20BIT				(1<<4)
+	#define SUNXI_HDMIAUDIOFAT0_SR_24BIT				(2<<4)
+	#define SUNXI_HDMIAUDIOFAT0_WSS_16BCLK			(0<<2)
+	#define SUNXI_HDMIAUDIOFAT0_WSS_20BCLK			(1<<2)
+	#define SUNXI_HDMIAUDIOFAT0_WSS_24BCLK			(2<<2)
+	#define SUNXI_HDMIAUDIOFAT0_WSS_32BCLK			(3<<2)
+	#define SUNXI_HDMIAUDIOFAT0_FMT_I2S				(0<<0)
+	#define SUNXI_HDMIAUDIOFAT0_FMT_LFT				(1<<0)
+	#define SUNXI_HDMIAUDIOFAT0_FMT_RGT				(2<<0)
+	#define SUNXI_HDMIAUDIOFAT0_FMT_RVD				(3<<0)
+
+#define SUNXI_HDMIAUDIOFAT1		(0x08)
+	#define SUNXI_HDMIAUDIOFAT1_SYNCLEN_16BCLK		(0<<12)
+	#define SUNXI_HDMIAUDIOFAT1_SYNCLEN_32BCLK		(1<<12)
+	#define SUNXI_HDMIAUDIOFAT1_SYNCLEN_64BCLK		(2<<12)
+	#define SUNXI_HDMIAUDIOFAT1_SYNCLEN_128BCLK		(3<<12)
+	#define SUNXI_HDMIAUDIOFAT1_SYNCLEN_256BCLK		(4<<12)
+	#define SUNXI_HDMIAUDIOFAT1_SYNCOUTEN			(1<<11)
+	#define SUNXI_HDMIAUDIOFAT1_OUTMUTE 				(1<<10)
+	#define SUNXI_HDMIAUDIOFAT1_MLS		 			(1<<9)
+	#define SUNXI_HDMIAUDIOFAT1_SEXT		 			(1<<8)
+	#define SUNXI_HDMIAUDIOFAT1_SI_1ST				(0<<6)
+	#define SUNXI_HDMIAUDIOFAT1_SI_2ND			 	(1<<6)
+	#define SUNXI_HDMIAUDIOFAT1_SI_3RD			 	(2<<6)
+	#define SUNXI_HDMIAUDIOFAT1_SI_4TH			 	(3<<6)
+	#define SUNXI_HDMIAUDIOFAT1_SW			 		(1<<5)
+	#define SUNXI_HDMIAUDIOFAT1_SSYNC	 			(1<<4)
+	#define SUNXI_HDMIAUDIOFAT1_RXPDM_16PCM			(0<<2)
+	#define SUNXI_HDMIAUDIOFAT1_RXPDM_8PCM			(1<<2)
+	#define SUNXI_HDMIAUDIOFAT1_RXPDM_8ULAW			(2<<2)
+	#define SUNXI_HDMIAUDIOFAT1_RXPDM_8ALAW  		(3<<2)
+	#define SUNXI_HDMIAUDIOFAT1_TXPDM_16PCM			(0<<0)
+	#define SUNXI_HDMIAUDIOFAT1_TXPDM_8PCM			(1<<0)
+	#define SUNXI_HDMIAUDIOFAT1_TXPDM_8ULAW			(2<<0)
+	#define SUNXI_HDMIAUDIOFAT1_TXPDM_8ALAW  		(3<<0)
+
+#define SUNXI_HDMIAUDIOTXFIFO 	(0x0C)
+
+#define SUNXI_HDMIAUDIORXFIFO 	(0x10)
+
+#define SUNXI_HDMIAUDIOFCTL  	(0x14)
+	#define SUNXI_HDMIAUDIOFCTL_FIFOSRC			(1<<31)
+	#define SUNXI_HDMIAUDIOFCTL_FTX				(1<<25)
+	#define SUNXI_HDMIAUDIOFCTL_FRX				(1<<24)
+	#define SUNXI_HDMIAUDIOFCTL_TXTL(v)			((v)<<12)
+	#define SUNXI_HDMIAUDIOFCTL_RXTL(v)  		((v)<<4)
+	#define SUNXI_HDMIAUDIOFCTL_TXIM_MOD0		(0<<2)
+	#define SUNXI_HDMIAUDIOFCTL_TXIM_MOD1		(1<<2)
+	#define SUNXI_HDMIAUDIOFCTL_RXOM_MOD0		(0<<0)
+	#define SUNXI_HDMIAUDIOFCTL_RXOM_MOD1		(1<<0)
+	#define SUNXI_HDMIAUDIOFCTL_RXOM_MOD2		(2<<0)
+	#define SUNXI_HDMIAUDIOFCTL_RXOM_MOD3		(3<<0)
+
+#define SUNXI_HDMIAUDIOFSTA   	(0x18)
+	#define SUNXI_HDMIAUDIOFSTA_TXE				(1<<28)
+	#define SUNXI_HDMIAUDIOFSTA_TXECNT(v)		((v)<<16)
+	#define SUNXI_HDMIAUDIOFSTA_RXA				(1<<8)
+	#define SUNXI_HDMIAUDIOFSTA_RXACNT(v)		((v)<<0)
+
+#define SUNXI_HDMIAUDIOINT    	(0x1C)
+	#define SUNXI_HDMIAUDIOINT_TXDRQEN				(1<<7)
+	#define SUNXI_HDMIAUDIOINT_TXUIEN				(1<<6)
+	#define SUNXI_HDMIAUDIOINT_TXOIEN				(1<<5)
+	#define SUNXI_HDMIAUDIOINT_TXEIEN				(1<<4)
+	#define SUNXI_HDMIAUDIOINT_RXDRQEN				(1<<2)
+	#define SUNXI_HDMIAUDIOINT_RXOIEN				(1<<1)
+	#define SUNXI_HDMIAUDIOINT_RXAIEN				(1<<0)
+
+#define SUNXI_HDMIAUDIOISTA   	(0x20)
+	#define SUNXI_HDMIAUDIOISTA_TXUISTA			(1<<6)
+	#define SUNXI_HDMIAUDIOISTA_TXOISTA			(1<<5)
+	#define SUNXI_HDMIAUDIOISTA_TXEISTA			(1<<4)
+	#define SUNXI_HDMIAUDIOISTA_RXOISTA			(1<<1)
+	#define SUNXI_HDMIAUDIOISTA_RXAISTA			(1<<0)
+
+#define SUNXI_HDMIAUDIOCLKD   	(0x24)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKOEN			(1<<7)
+	#define SUNXI_HDMIAUDIOCLKD_BCLKDIV_2		(0<<4)
+	#define SUNXI_HDMIAUDIOCLKD_BCLKDIV_4		(1<<4)
+	#define SUNXI_HDMIAUDIOCLKD_BCLKDIV_6		(2<<4)
+	#define SUNXI_HDMIAUDIOCLKD_BCLKDIV_8		(3<<4)
+	#define SUNXI_HDMIAUDIOCLKD_BCLKDIV_12		(4<<4)
+	#define SUNXI_HDMIAUDIOCLKD_BCLKDIV_16		(5<<4)
+	#define SUNXI_HDMIAUDIOCLKD_BCLKDIV_32		(6<<4)
+	#define SUNXI_HDMIAUDIOCLKD_BCLKDIV_64		(7<<4)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_1		(0<<0)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_2		(1<<0)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_4		(2<<0)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_6		(3<<0)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_8		(4<<0)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_12		(5<<0)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_16		(6<<0)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_24		(7<<0)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_32		(8<<0)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_48		(9<<0)
+	#define SUNXI_HDMIAUDIOCLKD_MCLKDIV_64		(10<<0)
+
+#define SUNXI_HDMIAUDIOTXCNT  	(0x28)
+
+#define SUNXI_HDMIAUDIORXCNT  	(0x2C)
+
+#define SUNXI_TXCHSEL		(0x30)
+	#define SUNXI_TXCHSEL_CHNUM(v)			(((v)-1)<<0)
+
+#define SUNXI_TXCHMAP		(0x34)
+	#define SUNXI_TXCHMAP_CH7(v)			(((v)-1)<<28)
+	#define SUNXI_TXCHMAP_CH6(v)			(((v)-1)<<24)
+	#define SUNXI_TXCHMAP_CH5(v)			(((v)-1)<<20)
+	#define SUNXI_TXCHMAP_CH4(v)			(((v)-1)<<16)
+	#define SUNXI_TXCHMAP_CH3(v)			(((v)-1)<<12)
+	#define SUNXI_TXCHMAP_CH2(v)			(((v)-1)<<8)
+	#define SUNXI_TXCHMAP_CH1(v)			(((v)-1)<<4)
+	#define SUNXI_TXCHMAP_CH0(v)			(((v)-1)<<0)
+
+#define SUNXI_RXCHSEL		(0x38)
+	#define SUNXI_RXCHSEL_CHNUM(v)			(((v)-1)<<0)
+
+#define SUNXI_RXCHMAP		(0x3C)
+	#define SUNXI_RXCHMAP_CH3(v)			(((v)-1)<<12)
+	#define SUNXI_RXCHMAP_CH2(v)			(((v)-1)<<8)
+	#define SUNXI_RXCHMAP_CH1(v)			(((v)-1)<<4)
+	#define SUNXI_RXCHMAP_CH0(v)			(((v)-1)<<0)
+
+
+/* DMA REGISTER */
+#define SUNXI_DMABASE	(0x01C02000)
+
+#define SUNXI_DMAIRQEN						(0x0)
+	#define SUNXI_DMAIRQEN_NDMA_FULLEN(v)				(1<<((v)*2+1))
+	#define SUNXI_DMAIRQEN_NDMA_HALFEN(v)				(1<<((v)*2))
+
+#define SUNXI_DMAIRQPENDING	 		(0x4)
+	#define SUNXI_DMAIRQPENGDING_NDMA_FULLPEND(v)		(1<<((v)*2+1))
+	#define SUNXI_DMAIRQPENGDING_NDMA_HALFPEND(v)		(1<<((v)*2))
+
+#define SUNXI_NDMACFG(v)				((v)*0x20+0x100)
+	#define SUNXI_NDMACFG_DMALOAD					(1<<31)
+	#define SUNXI_NDMACFG_BUSY						(1<<30)
+	#define SUNXI_NDMACFG_CONTINUOUS				(1<<29)
+	#define SUNXI_NDMACFG_WAIT(v)					(((v)-1)<<26)   //wait clock = 2^n  example: 8 clocks = 2^3
+	#define SUNXI_NDMACFG_DSTDATAWIDTH_8BIT		(0<<24)
+	#define SUNXI_NDMACFG_DSTDATAWIDTH_16BIT		(1<<24)
+	#define SUNXI_NDMACFG_DSTDATAWIDTH_32BIT		(2<<24)
+	#define SUNXI_NDMACFG_DSTDATAWIDTH_RVD			(3<<24)
+	#define SUNXI_NDMACFG_DSTBURST4				(1<<23)
+	#define SUNXI_NDMACFG_DSTADDRTYPE_INC			(0<<21)
+	#define SUNXI_NDMACFG_DSTADDRTYPE_CON 			(1<<21)
+	#define SUNXI_NDMACFG_DSTTYPE_IRTX				(0x0<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_SPDIFTX			(0x1<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_IISTX			(0x2<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_AC97TX			(0x3<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_SPI0TX 			(0x4<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_SPI1TX			(0x5<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_SPI2TX			(0x6<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_UART0TX			(0x8<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_UART1TX			(0x9<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_UART2TX			(0xA<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_UART3TX			(0xB<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_AUDIODA			(0xC<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_NFC				(0xF<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_SRAM				(0x10<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_DRAM				(0x11<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_UART4TX			(0x12<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_UART5TX          (0x13<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_UART6TX			(0x14<<16)
+	#define SUNXI_NDMACFG_DSTTYPE_UART7TX			(0x15<<16)
+	#define SUNXI_NDMACFG_SRCDATAWIDTH_8BIT		(0<<8)
+	#define SUNXI_NDMACFG_SRCDATAWIDTH_16BIT		(1<<8)
+	#define SUNXI_NDMACFG_SRCDATAWIDTH_32BIT		(2<<8)
+	#define SUNXI_NDMACFG_SRCDATAWIDTH_RVD			(3<<8)
+	#define SUNXI_NDMACFG_SRCBURST4				(1<<7)
+	#define SUNXI_NDMACFG_SRCADDRTYPE_INC			(0<<5)
+	#define SUNXI_NDMACFG_SRCADDRTYPE_CON 			(1<<5)
+	#define SUNXI_NDMACFG_SRCTYPE_IRRX				(0x0<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_SPDIFRX			(0x1<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_IISRX			(0x2<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_AC97RX			(0x3<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_SPI0RX 			(0x4<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_SPI1RX			(0x5<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_SPI2RX			(0x6<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_UART0RX			(0x8<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_UART1RX			(0x9<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_UART2RX			(0xA<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_UART3RX			(0xB<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_AUDIOAD			(0xC<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_TPAD				(0xD<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_NFC				(0xF<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_SRAM				(0x10<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_DRAM				(0x11<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_UART4RX			(0x12<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_UART5RX			(0x13<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_UART6RX			(0x14<<0)
+	#define SUNXI_NDMACFG_SRCTYPE_UART7RX			(0x15<<0)
+
+#define SUNXI_NDMASRCADDR(v)				((v)*0x20 + 0x100 + 4)
+
+#define SUNXI_NDMADSTADDR(v)				((v)*0x20 + 0x100 + 8)
+
+#define SUNXI_NDMACNT(v)				((v)*0x20 + 0x100 + 0xC)
+
+
+/* CCM REGISTER */
+#define SUNXI_CCMBASE    (0x01C20000)
+
+#define SUNXI_CCM_AUDIO_HOSC_PLL_REG   (0x08)
+	#define SUNXI_CCM_AUDIO_HOSC_PLL_REG_AUDIOEN		(1<<31)
+	#define SUNXI_CCM_AUDIO_HOSC_PLL_REG_FRE225792MHZ	(0<<27)
+	#define SUNXI_CCM_AUDIO_HOSC_PLL_REG_FRE24576MHZ	(1<<27)
+
+#define SUNXI_CCM_APB_GATE_REG    		 (0x68)
+	#define SUNXI_CCM_APB_GATE_REG_IISGATE				(1<<3)
+
+#define SUNXI_CCM_AUDIO_CLK_REG				(0xb8)
+	#define SUNXI_CCM_AUDIO_CLK_REG_IISSPECIALGATE		(1<<31)
+	#define SUNXI_CCM_AUDIO_CLK_REG_DIV(v)					((v)<<16)
+/*------------------------------------------------------------*/
+
+/*------------------------------------------------------------*/
+/* Clock dividers */
+#define SUNXI_DIV_MCLK	0
+#define SUNXI_DIV_BCLK	1
+
+#define SUNXI_HDMIAUDIOCLKD_MCLK_MASK   0x0f
+#define SUNXI_HDMIAUDIOCLKD_MCLK_OFFS   0
+#define SUNXI_HDMIAUDIOCLKD_BCLK_MASK   0x070
+#define SUNXI_HDMIAUDIOCLKD_BCLK_OFFS   4
+#define SUNXI_HDMIAUDIOCLKD_MCLKEN_OFFS 7
+
+unsigned int sunxi_hdmiaudio_get_clockrate(void);
+extern struct sunxi_hdmiaudio_info sunxi_hdmiaudio;
+
+extern void sunxi_snd_txctrl_hdmiaudio(struct snd_pcm_substream *substream, int on);
+extern void sunxi_snd_rxctrl_hdmiaudio(struct snd_pcm_substream *substream, int on);
+
+struct sunxi_hdmiaudio_info {
+	void __iomem   *regs;    /* IIS BASE */
+	void __iomem   *ccmregs;  //CCM BASE
+	void __iomem   *ioregs;   //IO BASE
+
+	u32 slave;					//0: master, 1: slave
+	u32 mono;					//0: stereo, 1: mono
+	u32 samp_fs;				//audio sample rate (unit in kHz)
+	u32 samp_res;			//16 bits, 20 bits , 24 bits, 32 bits)
+	u32 samp_format;		//audio sample format (0: standard I2S, 1: left-justified, 2: right-justified, 3: pcm)
+	u32 ws_size;				//16 BCLK, 20 BCLK, 24 BCLK, 32 BCLK)
+	u32 mclk_rate;			//mclk frequency divide by fs (128fs, 192fs, 256fs, 384fs, 512fs, 768fs)
+	u32 lrc_pol;				//LRC clock polarity (0: normal ,1: inverted)
+	u32 bclk_pol;			//BCLK polarity (0: normal, 1: inverted)
+	u32 pcm_txtype;		//PCM transmitter type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law)
+	u32 pcm_rxtype;		//PCM receiver type  (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law)
+	u32 pcm_sw;				//PCM slot width (8: 8 bits, 16: 16 bits)
+	u32 pcm_sync_period;//PCM sync period (16/32/64/128/256)
+	u32 pcm_sync_type;	//PCM sync symbol size (0: short sync, 1: long sync)
+	u32 pcm_start_slot;//PCM start slot index (1--4)
+	u32 pcm_lsb_first;	//0: MSB first, 1: LSB first
+	u32 pcm_ch_num;		//PCM channel number (1: one channel, 2: two channel)
+
+};
+
+extern struct sunxi_hdmiaudio_info sunxi_hdmiaudio;
+#endif
diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.c b/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.c
index 4916b51..91004e3 100644
--- a/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.c
+++ b/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.c
@@ -29,30 +29,29 @@
 #include <mach/hardware.h>
 #include <plat/dma_compat.h>
 
-static volatile unsigned int dmasrc;
-static volatile unsigned int dmadst;
+#include "sunxi-hdmiaudio.h"
+#include "sunxi-hdmipcm.h"
+
+static volatile unsigned int dmasrc = 0;
+static volatile unsigned int dmadst = 0;
 
 static const struct snd_pcm_hardware sunxi_pcm_hardware = {
-	.info			= SNDRV_PCM_INFO_INTERLEAVED |
-					SNDRV_PCM_INFO_BLOCK_TRANSFER |
-					SNDRV_PCM_INFO_MMAP |
-					SNDRV_PCM_INFO_MMAP_VALID |
-					SNDRV_PCM_INFO_PAUSE |
-					SNDRV_PCM_INFO_RESUME,
+	.info			= SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				      SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+				      SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
 	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
-					SNDRV_PCM_FMTBIT_S32_LE,
-	.rates			= SNDRV_PCM_RATE_8000_192000 |
-					SNDRV_PCM_RATE_KNOT,
+				      SNDRV_PCM_FMTBIT_S32_LE,
+	.rates			= SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT,
 	.rate_min		= 8000,
 	.rate_max		= 192000,
 	.channels_min		= 1,
 	.channels_max		= 2,
-	.buffer_bytes_max	= 128*1024, /* value must be (2^n)Kbyte size */
-	.period_bytes_min	= 1024*4,
-	.period_bytes_max	= 1024*32,
-	.periods_min		= 4,
-	.periods_max		= 8,
-	.fifo_size		= 128,
+	.buffer_bytes_max	= 128*1024,    /* value must be (2^n)Kbyte size */
+	.period_bytes_min	= 1024*4,//1024*4,
+	.period_bytes_max	= 1024*32,//1024*32,
+	.periods_min		= 4,//4,
+	.periods_max		= 8,//8,
+	.fifo_size		= 128,//32,
 };
 
 struct sunxi_runtime_data {
@@ -75,19 +74,23 @@ static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream)
 	int ret;
 
 	unsigned long len = prtd->dma_period;
-	limit = prtd->dma_limit;
-	while (prtd->dma_loaded < limit) {
-		if ((pos + len) > prtd->dma_end)
-			len = prtd->dma_end - pos;
+  	limit = prtd->dma_limit;
+  	while(prtd->dma_loaded < limit)
+	{
+		if((pos + len) > prtd->dma_end){
+			len  = prtd->dma_end - pos;
+		}
 
 		ret = sunxi_dma_enqueue(prtd->params, pos, len, 0);
 		if (ret == 0) {
 			prtd->dma_loaded++;
 			pos += prtd->dma_period;
-			if (pos >= prtd->dma_end)
+			if(pos >= prtd->dma_end)
 				pos = prtd->dma_start;
-		} else
+		}else {
 			break;
+		}
+
 	}
 	prtd->dma_pos = pos;
 }
@@ -98,8 +101,9 @@ static void sunxi_audio_buffdone(struct sunxi_dma_params *dma, void *dev_id)
 	struct snd_pcm_substream *substream = dev_id;
 
 	prtd = substream->runtime->private_data;
-	if (substream)
-		snd_pcm_period_elapsed(substream);
+		if (substream) {
+			snd_pcm_period_elapsed(substream);
+		}
 
 	spin_lock(&prtd->lock);
 	{
@@ -126,12 +130,13 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream,
 	if (prtd->params == NULL) {
 		prtd->params = dma;
 		ret = sunxi_dma_request(prtd->params, 1);
-		if (ret < 0)
-			return ret;
+		if (ret < 0) {
+				return ret;
+		}
 	}
 
 	if (sunxi_dma_set_callback(prtd->params, sunxi_audio_buffdone,
-							substream) != 0) {
+							    substream) != 0) {
 		sunxi_dma_release(prtd->params);
 		prtd->params = NULL;
 		return -EINVAL;
@@ -178,25 +183,25 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream)
 	if (!prtd->params)
 		return 0;
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+   	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
 #if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I
 		struct dma_hw_conf codec_dma_conf;
-		codec_dma_conf.drqsrc_type	= DRQ_TYPE_SDRAM;
-		codec_dma_conf.drqdst_type	= DRQ_TYPE_HDMIAUDIO;
-		codec_dma_conf.xfer_type	= DMAXFER_D_BWORD_S_BWORD;
-		codec_dma_conf.address_type	= DMAADDRT_D_IO_S_LN;
-		codec_dma_conf.dir		= SW_DMA_WDEV;
-		codec_dma_conf.reload		= 0;
-		codec_dma_conf.hf_irq		= SW_DMA_IRQ_FULL;
-		codec_dma_conf.from		= prtd->dma_start;
-		codec_dma_conf.to		= prtd->params->dma_addr;
+		codec_dma_conf.drqsrc_type  = DRQ_TYPE_SDRAM;
+		codec_dma_conf.drqdst_type  = DRQ_TYPE_HDMIAUDIO;
+		codec_dma_conf.xfer_type    = DMAXFER_D_BWORD_S_BWORD;
+		codec_dma_conf.address_type = DMAADDRT_D_IO_S_LN;
+		codec_dma_conf.dir          = SW_DMA_WDEV;
+		codec_dma_conf.reload       = 0;
+		codec_dma_conf.hf_irq       = SW_DMA_IRQ_FULL;
+		codec_dma_conf.from         = prtd->dma_start;
+		codec_dma_conf.to           = prtd->params->dma_addr;
 #else
 		dma_config_t codec_dma_conf;
 		memset(&codec_dma_conf, 0, sizeof(codec_dma_conf));
 		codec_dma_conf.xfer_type.src_data_width	= DATA_WIDTH_32BIT;
-		codec_dma_conf.xfer_type.src_bst_len	= DATA_BRST_4;
+		codec_dma_conf.xfer_type.src_bst_len 	= DATA_BRST_4;
 		codec_dma_conf.xfer_type.dst_data_width	= DATA_WIDTH_32BIT;
-		codec_dma_conf.xfer_type.dst_bst_len	= DATA_BRST_4;
+		codec_dma_conf.xfer_type.dst_bst_len 	= DATA_BRST_4;
 		codec_dma_conf.address_type.src_addr_mode = DDMA_ADDR_LINEAR;
 		codec_dma_conf.address_type.dst_addr_mode = DDMA_ADDR_IO;
 		codec_dma_conf.src_drq_type		= D_SRC_SDRAM;
@@ -210,8 +215,8 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream)
 
 	/* flush the DMA channel */
 	prtd->dma_loaded = 0;
-	if (sunxi_dma_flush(prtd->params) == 0)
-		prtd->dma_pos = prtd->dma_start;
+	sunxi_dma_flush(prtd->params);
+	prtd->dma_pos = prtd->dma_start;
 
 	/* enqueue dma buffers */
 	sunxi_pcm_enqueue(substream);
@@ -258,19 +263,21 @@ static snd_pcm_uframes_t sunxi_pcm_pointer(struct snd_pcm_substream *substream)
 	snd_pcm_uframes_t offset = 0;
 
 	spin_lock(&prtd->lock);
-	sunxi_dma_getcurposition(prtd->params, (dma_addr_t *)&dmasrc,
-				 (dma_addr_t *)&dmadst);
+
+	sunxi_dma_getcurposition(prtd->params, (dma_addr_t*)&dmasrc,
+				 (dma_addr_t*)&dmadst);
 
 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
 		res = dmadst - prtd->dma_start;
 	else
-		offset = bytes_to_frames(runtime, dmasrc + prtd->dma_period -
-							runtime->dma_addr);
+	{
+		offset = bytes_to_frames(runtime, dmasrc + prtd->dma_period - runtime->dma_addr);
+	}
 	spin_unlock(&prtd->lock);
 
-	if (offset >= runtime->buffer_size)
+	if(offset >= runtime->buffer_size)
 		offset = 0;
-	return offset;
+		return offset;
 }
 
 static int sunxi_pcm_open(struct snd_pcm_substream *substream)
@@ -307,21 +314,21 @@ static int sunxi_pcm_mmap(struct snd_pcm_substream *substream,
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
 	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
-							runtime->dma_area,
-							runtime->dma_addr,
-							runtime->dma_bytes);
+				     runtime->dma_area,
+				     runtime->dma_addr,
+				     runtime->dma_bytes);
 }
 
 static struct snd_pcm_ops sunxi_pcm_ops = {
-	.open			= sunxi_pcm_open,
+	.open				= sunxi_pcm_open,
 	.close			= sunxi_pcm_close,
 	.ioctl			= snd_pcm_lib_ioctl,
-	.hw_params		= sunxi_pcm_hw_params,
+	.hw_params	= sunxi_pcm_hw_params,
 	.hw_free		= sunxi_pcm_hw_free,
 	.prepare		= sunxi_pcm_prepare,
 	.trigger		= sunxi_pcm_trigger,
 	.pointer		= sunxi_pcm_pointer,
-	.mmap			= sunxi_pcm_mmap,
+	.mmap				= sunxi_pcm_mmap,
 };
 
 static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
@@ -334,7 +341,7 @@ static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 	buf->dev.dev = pcm->card->dev;
 	buf->private_data = NULL;
 	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
-							&buf->addr, GFP_KERNEL);
+					   &buf->addr, GFP_KERNEL);
 	if (!buf->area)
 		return -ENOMEM;
 	buf->bytes = size;
@@ -343,9 +350,9 @@ static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 
 static void sunxi_pcm_free_dma_buffers(struct snd_pcm *pcm)
 {
-	int stream;
-	struct snd_dma_buffer *buf;
 	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
 
 	for (stream = 0; stream < 2; stream++) {
 		substream = pcm->streams[stream].substream;
@@ -357,7 +364,7 @@ static void sunxi_pcm_free_dma_buffers(struct snd_pcm *pcm)
 			continue;
 
 		dma_free_writecombine(pcm->card->dev, buf->bytes,
-							buf->area, buf->addr);
+				      buf->area, buf->addr);
 		buf->area = NULL;
 	}
 }
@@ -366,9 +373,9 @@ static u64 sunxi_pcm_mask = DMA_BIT_MASK(32);
 
 static int sunxi_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
 	int ret = 0;
-	struct snd_pcm *pcm	= rtd->pcm;
-	struct snd_card *card	= rtd->card->snd_card;
 
 	if (!card->dev->dma_mask)
 		card->dev->dma_mask = &sunxi_pcm_mask;
@@ -393,15 +400,14 @@ static int sunxi_pcm_new(struct snd_soc_pcm_runtime *rtd)
 }
 
 static struct snd_soc_platform_driver sunxi_soc_platform_hdmiaudio = {
-		.ops		=		&sunxi_pcm_ops,
-		.pcm_new	=		sunxi_pcm_new,
-		.pcm_free	=		sunxi_pcm_free_dma_buffers,
+		.ops        =        &sunxi_pcm_ops,
+		.pcm_new	=		 sunxi_pcm_new,
+		.pcm_free	=		 sunxi_pcm_free_dma_buffers,
 };
 
 static int __devinit sunxi_hdmiaudio_pcm_probe(struct platform_device *pdev)
 {
-	return snd_soc_register_platform(&pdev->dev,
-						&sunxi_soc_platform_hdmiaudio);
+	return snd_soc_register_platform(&pdev->dev, &sunxi_soc_platform_hdmiaudio);
 }
 
 static int __devexit sunxi_hdmiaudio_pcm_remove(struct platform_device *pdev)
@@ -411,9 +417,9 @@ static int __devexit sunxi_hdmiaudio_pcm_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver sunxi_hdmiaudio_pcm_driver = {
-	.probe	= sunxi_hdmiaudio_pcm_probe,
-	.remove	= __devexit_p(sunxi_hdmiaudio_pcm_remove),
-	.driver	= {
+	.probe = sunxi_hdmiaudio_pcm_probe,
+	.remove = __devexit_p(sunxi_hdmiaudio_pcm_remove),
+	.driver = {
 		.name = "sunxi-hdmiaudio-pcm-audio",
 		.owner = THIS_MODULE,
 	},
@@ -423,8 +429,7 @@ static struct platform_driver sunxi_hdmiaudio_pcm_driver = {
 static int __init sunxi_soc_platform_hdmiaudio_init(void)
 {
 	int err = 0;
-	err = platform_driver_register(&sunxi_hdmiaudio_pcm_driver);
-	if (err < 0)
+	if ((err = platform_driver_register(&sunxi_hdmiaudio_pcm_driver)) < 0)
 		return err;
 	return 0;
 }
diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.h b/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.h
new file mode 100644
index 0000000..0649177
--- /dev/null
+++ b/sound/soc/sunxi/hdmiaudio/sunxi-hdmipcm.h
@@ -0,0 +1,25 @@
+/*
+ * sound\soc\sunxi\hdmiaudio\sunxi-hdmipcm.h
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * chenpailin <chenpailin@allwinnertech.com>
+ *
+ * some simple description for this code
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef SUNXI_HDMIPCM_H_
+#define SUNXI_HDMIPCM_H_
+
+enum sunxi_dma_buffresult {
+	SUNXI_RES_OK,
+	SUNXI_RES_ERR,
+	SUNXI_RES_ABORT
+};
+
+#endif //SUNXI_HDMIPCM_H_
diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.c b/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.c
index 75157fa..c89f760 100644
--- a/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.c
+++ b/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.c
@@ -24,27 +24,213 @@
 #include <plat/sys_config.h>
 #include <linux/io.h>
 
+#include "sunxi-hdmiaudio.h"
+#include "sunxi-hdmipcm.h"
+
+#include "sndhdmi.h"
+
+static struct clk *xtal;
+
+static int clk_users;
+static DEFINE_MUTEX(clk_lock);
+
+#ifdef ENFORCE_RATES
+static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
+	.count	= ARRAY_SIZE(rates),
+	.list	= rates,
+	.mask	= 0,
+};
+#endif
+
+static int sunxi_sndhdmi_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	#ifdef ENFORCE_RATES
+		struct snd_pcm_runtime *runtime = substream->runtime;;
+	#endif
+	mutex_lock(&clk_lock);
+	mutex_unlock(&clk_lock);
+	if (!ret) {
+	#ifdef ENFORCE_RATES
+		ret = snd_pcm_hw_constraint_list(runtime, 0,
+						 SNDRV_PCM_HW_PARAM_RATE,
+						 &hw_constraints_rates);
+		if (ret < 0)
+
+	#endif
+	}
+	return ret;
+}
+
+static void sunxi_sndhdmi_shutdown(struct snd_pcm_substream *substream)
+{
+	mutex_lock(&clk_lock);
+	clk_users -= 1;
+	if (clk_users == 0) {
+		clk_put(xtal);
+		xtal = NULL;
+	}
+	mutex_unlock(&clk_lock);
+}
+
+typedef struct __MCLK_SET_INF
+{
+    __u32       samp_rate;      // sample rate
+    __u16       mult_fs;        // multiply of smaple rate
+
+    __u8        clk_div;        // mpll division
+    __u8        mpll;           // select mpll, 0 - 24.576 Mhz, 1 - 22.5792 Mhz
+
+} __mclk_set_inf;
+
+typedef struct __BCLK_SET_INF
+{
+    __u8        bitpersamp;     // bits per sample
+    __u8        clk_div;        // clock division
+    __u16       mult_fs;        // multiplay of sample rate
+
+} __bclk_set_inf;
+
+//bclk divider table
+static __bclk_set_inf BCLK_INF[] =
+{
+    // 16bits per sample
+    {16,  4, 128}, {16,  6, 192}, {16,  8, 256},
+    {16, 12, 384}, {16, 16, 512},
+
+    //24 bits per sample
+    {24,  4, 192}, {24,  8, 384}, {24, 16, 768},
+
+    //32 bits per sample
+    {32,  2, 128}, {32,  4, 256}, {32,  6, 384},
+    {32,  8, 512}, {32, 12, 768},
+
+    //end flag
+    {0xff, 0, 0},
+};
+
+//mclk divider table
+static __mclk_set_inf  MCLK_INF[] =
+{
+    // 8k bitrate
+    {  8000, 128, 24, 0}, {  8000, 192, 16, 0}, {  8000, 256, 12, 0},
+    {  8000, 384,  8, 0}, {  8000, 512,  6, 0}, {  8000, 768,  4, 0},
+
+    // 16k bitrate
+    { 16000, 128, 12, 0}, { 16000, 192,  8, 0}, { 16000, 256,  6, 0},
+    { 16000, 384,  4, 0}, { 16000, 768,  2, 0},
+
+    // 32k bitrate
+    { 32000, 128,  6, 0}, { 32000, 192,  4, 0}, { 32000, 384,  2, 0},
+    { 32000, 768,  1, 0},
+
+    // 64k bitrate
+    { 64000, 192,  2, 0}, { 64000, 384,  1, 0},
+
+    //128k bitrate
+    {128000, 192,  1, 0},
+
+    // 12k bitrate
+    { 12000, 128, 16, 0}, { 12000, 256, 8, 0}, { 12000, 512, 4, 0},
+
+    // 24k bitrate
+    { 24000, 128,  8, 0}, { 24000, 256, 4, 0}, { 24000, 512, 2, 0},
+
+    // 48K bitrate
+    { 48000, 128,  4, 0}, { 48000, 256,  2, 0}, { 48000, 512, 1, 0},
+
+    // 96k bitrate
+    { 96000, 128 , 2, 0}, { 96000, 256,  1, 0},
+
+    //192k bitrate
+    {192000, 128,  1, 0},
+
+    //11.025k bitrate
+    { 11025, 128, 16, 1}, { 11205, 256,  8, 1}, { 11205, 512,  4, 1},
+
+    //22.05k bitrate
+    { 22050, 128,  8, 1}, { 22050, 256,  4, 1},
+    { 22050, 512,  2, 1},
+
+    //44.1k bitrate
+    { 44100, 128,  4, 1}, { 44100, 256,  2, 1}, { 44100, 512,  1, 1},
+
+    //88.2k bitrate
+    { 88200, 128,  2, 1}, { 88200, 256,  1, 1},
+
+    //176.4k bitrate
+    {176400, 128, 1, 1},
+
+    //end flag 0xffffffff
+    {0xffffffff, 0, 0, 0},
+};
+
+static s32 get_clock_divder(u32 sample_rate, u32 sample_width, u32 * mclk_div,
+                            u32* mpll, u32* bclk_div, u32* mult_fs)
+{
+	u32 i, j, ret = -EINVAL;
+
+	for(i=0; i< 100; i++) {
+		 if((MCLK_INF[i].samp_rate == sample_rate) &&
+		 	((MCLK_INF[i].mult_fs == 256) || (MCLK_INF[i].mult_fs == 128))) {
+			  for(j=0; j<ARRAY_SIZE(BCLK_INF); j++) {
+					if((BCLK_INF[j].bitpersamp == sample_width) &&
+						(BCLK_INF[j].mult_fs == MCLK_INF[i].mult_fs)) {
+						 //set mclk and bclk division
+						 *mclk_div = MCLK_INF[i].clk_div;
+						 *mpll = MCLK_INF[i].mpll;
+						 *bclk_div = BCLK_INF[j].clk_div;
+						 *mult_fs = MCLK_INF[i].mult_fs;
+						 ret = 0;
+						 break;
+					}
+			  }
+		 }else if(MCLK_INF[i].samp_rate == 0xffffffff)
+		 	break;
+	}
+
+	return ret;
+}
+
 static int sunxi_sndhdmi_hw_params(struct snd_pcm_substream *substream,
 					struct snd_pcm_hw_params *params)
 {
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 	int ret = 0;
-	struct snd_soc_pcm_runtime *rtd;
-	struct snd_soc_dai *codec_dai;
-	struct snd_soc_dai *cpu_dai;
+	unsigned long rate = params_rate(params);
+	u32 mclk_div=0, mpll=0, bclk_div=0, mult_fs=0;
 
-	if (!substream) {
-		printk("error:%s,line:%d\n", __func__, __LINE__);
-		return -EAGAIN;
-	}
-	rtd		= substream->private_data;
-	codec_dai	= rtd->codec_dai;
-	cpu_dai		= rtd->cpu_dai;
+	get_clock_divder(rate, 32, &mclk_div, &mpll, &bclk_div, &mult_fs);
+
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(cpu_dai, 0 , mpll, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0 , mpll, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_MCLK, mclk_div);
+	if (ret < 0)
+		return ret;
 
-	ret = snd_soc_dai_set_fmt(codec_dai, 0);
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_BCLK, bclk_div);
 	if (ret < 0)
 		return ret;
 
-	ret = snd_soc_dai_set_fmt(cpu_dai, 0);
+	ret = snd_soc_dai_set_clkdiv(codec_dai, 0, mult_fs);
 	if (ret < 0)
 		return ret;
 
@@ -52,24 +238,26 @@ static int sunxi_sndhdmi_hw_params(struct snd_pcm_substream *substream,
 }
 
 static struct snd_soc_ops sunxi_sndhdmi_ops = {
-	.hw_params	= sunxi_sndhdmi_hw_params,
+	.startup 	= sunxi_sndhdmi_startup,
+	.shutdown 	= sunxi_sndhdmi_shutdown,
+	.hw_params 	= sunxi_sndhdmi_hw_params,
 };
 
 static struct snd_soc_dai_link sunxi_sndhdmi_dai_link = {
-	.name		= "HDMIAUDIO",
-	.stream_name	= "SUNXI-HDMIAUDIO",
-	.cpu_dai_name	= "sunxi-hdmiaudio.0",
+	.name 			= "HDMIAUDIO",
+	.stream_name 	= "SUNXI-HDMIAUDIO",
+	.cpu_dai_name 	= "sunxi-hdmiaudio.0",
 	.codec_dai_name = "sndhdmi",
-	.platform_name	= "sunxi-hdmiaudio-pcm-audio.0",
-	.codec_name	= "sunxi-hdmiaudio-codec.0",
-	.ops			= &sunxi_sndhdmi_ops,
+	.platform_name 	= "sunxi-hdmiaudio-pcm-audio.0",
+	.codec_name 	= "sunxi-hdmiaudio-codec.0",
+	.ops 			= &sunxi_sndhdmi_ops,
 };
 
 static struct snd_soc_card snd_soc_sunxi_sndhdmi = {
-	.name		= "sunxi-sndhdmi",
+	.name 		= "sunxi-sndhdmi",
 	.owner		= THIS_MODULE,
-	.dai_link	= &sunxi_sndhdmi_dai_link,
-	.num_links	= 1,
+	.dai_link 	= &sunxi_sndhdmi_dai_link,
+	.num_links 	= 1,
 };
 
 static int __devinit sunxi_sndhdmi_probe(struct platform_device *pdev)
diff --git a/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.h b/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.h
new file mode 100644
index 0000000..341aee9
--- /dev/null
+++ b/sound/soc/sunxi/hdmiaudio/sunxi-sndhdmi.h
@@ -0,0 +1,26 @@
+/*
+ * sound\soc\sunxi\hdmiaudio\sunxi-sndhdmi.h
+ * (C) Copyright 2007-2011
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ * chenpailin <chenpailin@allwinnertech.com>
+ *
+ * some simple description for this code
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef SUNXI_SNDHDMI_H_
+#define SUNXI_SNDHDMI_H_
+
+struct sunxi_sndhdmi_platform_data {
+	int hdmiaudio_bclk;
+	int hdmiaudio_ws;
+	int hdmiaudio_data;
+	void (*power)(int);
+	int model;
+}
+#endif
diff --git a/sound/soc/sunxi/i2s/Kconfig b/sound/soc/sunxi/i2s/Kconfig
index a6dff42..f244faa 100644
--- a/sound/soc/sunxi/i2s/Kconfig
+++ b/sound/soc/sunxi/i2s/Kconfig
@@ -1,5 +1,5 @@
 config SND_SUNXI_SOC_I2S_INTERFACE
-	tristate "SoC i2s interface for the AllWinner sun4i and sun5i chips"
+	tristate "SoC i2s interface for the AllWinner sun4i, sun5i and sun7i chips"
 	default m
 	help
 	  Say Y or M if you want to add support for codecs attached to
diff --git a/sound/soc/sunxi/i2s/sndi2s.c b/sound/soc/sunxi/i2s/sndi2s.c
index 68bad02..92e01a8 100644
--- a/sound/soc/sunxi/i2s/sndi2s.c
+++ b/sound/soc/sunxi/i2s/sndi2s.c
@@ -35,11 +35,20 @@ struct sndi2s_priv {
 };
 
 static int i2s_used = 0;
-#define sndi2s_RATES  (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT)
-#define sndi2s_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
-		                     SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
+static int sunxi_i2s_slave = 0;
+
+#define sndi2s_RATES_MASTER  (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT)
+#define sndi2s_RATES_SLAVE (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+				SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\
+				SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000|\
+				SNDRV_PCM_RATE_352800 | SNDRV_PCM_RATE_384000)
 
-hdmi_audio_t hdmi_parameter;
+#if defined CONFIG_ARCH_SUN7I
+#define sndi2s_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#else
+#define sndi2s_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+		                     SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_LE)
+#endif
 
 static int sndi2s_mute(struct snd_soc_dai *dai, int mute)
 {
@@ -62,8 +71,6 @@ static int sndi2s_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params,
 	struct snd_soc_dai *dai)
 {
-	hdmi_parameter.sample_rate = params_rate(params);
-
 	return 0;
 }
 
@@ -75,9 +82,6 @@ static int sndi2s_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 
 static int sndi2s_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div)
 {
-
-	hdmi_parameter.fs_between = div;
-
 	return 0;
 }
 
@@ -104,7 +108,14 @@ struct snd_soc_dai_driver sndi2s_dai = {
 		.stream_name = "Playback",
 		.channels_min = 1,
 		.channels_max = 2,
-		.rates = sndi2s_RATES,
+		.rates = sndi2s_RATES_MASTER,
+		.formats = sndi2s_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = sndi2s_RATES_MASTER,
 		.formats = sndi2s_FORMATS,
 	},
 	/* pcm operations */
@@ -129,10 +140,8 @@ static int sndi2s_soc_probe(struct snd_soc_codec *codec)
 /* power down chip */
 static int sndi2s_soc_remove(struct snd_soc_codec *codec)
 {
-	struct sndhdmi_priv *sndi2s = snd_soc_codec_get_drvdata(codec);
-
+	struct sndi2s_priv *sndi2s = snd_soc_codec_get_drvdata(codec);
 	kfree(sndi2s);
-
 	return 0;
 }
 
@@ -143,6 +152,13 @@ static struct snd_soc_codec_driver soc_codec_dev_sndi2s = {
 
 static int __devinit sndi2s_codec_probe(struct platform_device *pdev)
 {
+	if(sunxi_i2s_slave) {
+		sndi2s_dai.playback.rates = sndi2s_RATES_SLAVE;
+		sndi2s_dai.capture.rates = sndi2s_RATES_SLAVE;
+		printk("[I2S-0] sndi2s_codec_probe I2S used in slave mode\n");
+	}
+	else 
+		printk("[I2S-0] sndi2s_codec_probe I2S used in master mode\n");
 	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_sndi2s, &sndi2s_dai, 1);
 }
 
@@ -169,21 +185,31 @@ static struct platform_driver sndi2s_codec_driver = {
 static int __init sndi2s_codec_init(void)
 {
 	int err = 0;
-	int ret = 0;
+	int ret = 0, i2s_slave = 0;
 
 	ret = script_parser_fetch("i2s_para","i2s_used", &i2s_used, sizeof(int));
 	if (ret) {
-        printk("[I2S]sndi2s_init fetch i2s using configuration failed\n");
+        printk("[I2S-0] sndi2s_init fetch i2s using configuration failed\n");
     }
 
 	if (i2s_used) {
+		ret = script_parser_fetch("i2s_para","i2s_slave", &i2s_slave, sizeof(int));
+		if(ret == 0 && i2s_slave == 1) {
+			sunxi_i2s_slave = 1;
+			printk("[I2S-0] sndi2s_codec_init I2S used in slave mode\n");
+		}
+		else {
+			sunxi_i2s_slave = 0;
+			printk("[I2S-0] sndi2s_codec_init I2S used in master mode\n");
+		}
+	
 		if((err = platform_device_register(&sndi2s_codec_device)) < 0)
 			return err;
 
 		if ((err = platform_driver_register(&sndi2s_codec_driver)) < 0)
 			return err;
 	} else {
-       printk("[I2S]sndi2s cannot find any using configuration for controllers, return directly!\n");
+       printk("[I2S-0] sndi2s cannot find any using configuration for controllers, return directly!\n");
        return 0;
     }
 
@@ -204,3 +230,4 @@ MODULE_DESCRIPTION("SNDI2S ALSA soc codec driver");
 MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:sunxi-i2s-codec");
+
diff --git a/sound/soc/sunxi/i2s/sndi2s.h b/sound/soc/sunxi/i2s/sndi2s.h
index e5b95c8..57e2aa5 100644
--- a/sound/soc/sunxi/i2s/sndi2s.h
+++ b/sound/soc/sunxi/i2s/sndi2s.h
@@ -16,6 +16,8 @@
 #ifndef SNDI2S_H_
 #define SNDI2S_H_
 
+#if 0
+cleaning code
 typedef struct hdmi_audio
 {
 	__u8    hw_intf;        /* 0:iis  1:spdif 2:pcm */
@@ -54,5 +56,6 @@ typedef enum tag_HDMI_CMD
 		HDMI_CMD_AUDIO_ENABLE,
 		HDMI_CMD_GET_HPD_STATUS,
 }__hdmi_cmd_t;
+#endif
 
 #endif
diff --git a/sound/soc/sunxi/i2s/sunxi-i2s.c b/sound/soc/sunxi/i2s/sunxi-i2s.c
index b4dd3ad..f136f80 100644
--- a/sound/soc/sunxi/i2s/sunxi-i2s.c
+++ b/sound/soc/sunxi/i2s/sunxi-i2s.c
@@ -38,7 +38,7 @@
 #include "sunxi-i2sdma.h"
 #include "sunxi-i2s.h"
 
-static int regsave[8];
+static int regsave[10];
 static int i2s_used = 0;
 
 static struct sunxi_dma_params sunxi_i2s_pcm_stereo_out = {
@@ -58,13 +58,16 @@ static struct sunxi_dma_params sunxi_i2s_pcm_stereo_in = {
 };
 
 
- struct sunxi_i2s_info sunxi_iis;
+/* most of fields of this structure is never initialized and useless !!!*/
+struct sunxi_i2s_info sunxi_iis;
 static u32 i2s_handle = 0;
- static struct clk *i2s_apbclk, *i2s_pll2clk, *i2s_pllx8, *i2s_moduleclk;
+static struct clk *i2s_apbclk, *i2s_pll2clk, *i2s_pllx8, *i2s_moduleclk;
 
 void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on)
 {
 	u32 reg_val;
+	int res;
+	printk("[I2S-0] sunxi_snd_txctrl_i2s is on=(%d)\n", on);
 
 	reg_val = readl(sunxi_iis.regs + SUNXI_TXCHSEL);
 	reg_val &= ~0x7;
@@ -73,7 +76,7 @@ void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on)
 
 	reg_val = readl(sunxi_iis.regs + SUNXI_TXCHMAP);
 	reg_val = 0;
-	if (sunxi_is_sun4i()) {
+	if (sunxi_is_sun4i() || sunxi_is_sun7i()) {
 		if(substream->runtime->channels == 1) {
 			reg_val = 0x76543200;
 		} else {
@@ -89,7 +92,7 @@ void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on)
 	writel(reg_val, sunxi_iis.regs + SUNXI_TXCHMAP);
 
 	reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
-	if (sunxi_is_sun4i()) {
+	if (sunxi_is_sun4i() || sunxi_is_sun7i()) {
 		reg_val &= ~SUNXI_IISCTL_SDO3EN;
 		reg_val &= ~SUNXI_IISCTL_SDO2EN;
 		reg_val &= ~SUNXI_IISCTL_SDO1EN;
@@ -144,12 +147,13 @@ void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on)
 		reg_val |= SUNXI_IISINT_TXDRQEN;
 		writel(reg_val, sunxi_iis.regs + SUNXI_IISINT);
 
-		//Global Enable Digital Audio Interface
-		reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
-		reg_val |= SUNXI_IISCTL_GEN;
-		writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
+		//Disable mute
+		res = gpio_write_one_pin_value(i2s_handle, 0, "i2s_mute");
 
 	} else {
+		//Enable mute
+		res = gpio_write_one_pin_value(i2s_handle, 1, "i2s_mute");
+
 		/* IIS TX DISABLE */
 		reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
 		reg_val &= ~SUNXI_IISCTL_TXEN;
@@ -159,17 +163,26 @@ void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on)
 		reg_val = readl(sunxi_iis.regs + SUNXI_IISINT);
 		reg_val &= ~SUNXI_IISINT_TXDRQEN;
 		writel(reg_val, sunxi_iis.regs + SUNXI_IISINT);
-
-		//Global disable Digital Audio Interface
-		reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
-		reg_val &= ~SUNXI_IISCTL_GEN;
-		writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
 	}
 }
 
-void sunxi_snd_rxctrl_i2s(int on)
+void sunxi_snd_rxctrl_i2s(struct snd_pcm_substream *substream, int on)
 {
 	u32 reg_val;
+	printk("[I2S-0] sunxi_snd_rxctrl_i2s is on=(%d)\n", on);
+	reg_val = readl(sunxi_iis.regs + SUNXI_RXCHSEL);
+	reg_val &= ~0x7;
+	reg_val |= SUNXI_RXCHSEL_CHNUM(substream->runtime->channels);
+	writel(reg_val, sunxi_iis.regs + SUNXI_RXCHSEL);
+
+	reg_val = readl(sunxi_iis.regs + SUNXI_RXCHMAP);
+	reg_val = 0;
+	if(substream->runtime->channels == 1) {
+		reg_val = 0x00003200;
+	} else {
+		reg_val = 0x00003210;
+	}
+	writel(reg_val, sunxi_iis.regs + SUNXI_RXCHMAP);
 
 	//flush RX FIFO
 	reg_val = readl(sunxi_iis.regs + SUNXI_IISFCTL);
@@ -190,63 +203,52 @@ void sunxi_snd_rxctrl_i2s(int on)
 		reg_val |= SUNXI_IISINT_RXDRQEN;
 		writel(reg_val, sunxi_iis.regs + SUNXI_IISINT);
 
-		//Global Enable Digital Audio Interface
-		reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
-		reg_val |= SUNXI_IISCTL_GEN;
-		writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
-
 	} else {
 		/* IIS RX DISABLE */
 		reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
 		reg_val &= ~SUNXI_IISCTL_RXEN;
 		writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
 
-		/* DISBALE dma DRQ mode */
+		/* DISABLE dma DRQ mode */
 		reg_val = readl(sunxi_iis.regs + SUNXI_IISINT);
 		reg_val &= ~SUNXI_IISINT_RXDRQEN;
 		writel(reg_val, sunxi_iis.regs + SUNXI_IISINT);
-
-		//Global disable Digital Audio Interface
-		reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
-		reg_val &= ~SUNXI_IISCTL_GEN;
-		writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
 	}
 }
 
-static inline int sunxi_snd_is_clkmaster(void)
-{
-	return ((readl(sunxi_iis.regs + SUNXI_IISCTL) & SUNXI_IISCTL_MS) ? 0 : 1);
-}
-
 static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 {
 	u32 reg_val;
 	u32 reg_val1;
 
+	printk("[IIS-0] sunxi_i2s_set_fmt\n");
+
 	//SDO ON
 	reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
-	if (sunxi_is_sun4i()) {
+	if (sunxi_is_sun4i() || sunxi_is_sun7i()) {
 		reg_val |= (SUNXI_IISCTL_SDO0EN | SUNXI_IISCTL_SDO1EN |
 			    SUNXI_IISCTL_SDO2EN | SUNXI_IISCTL_SDO3EN);
 	} else {
 		reg_val |= SUNXI_IISCTL_SDO0EN;
 	}
-	writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
+
+	//writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
 
 	/* master or slave selection */
-	reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
-	switch(fmt & SND_SOC_DAIFMT_MASTER_MASK){
-		case SND_SOC_DAIFMT_CBM_CFM:   /* codec clk & frm master */
-			reg_val |= SUNXI_IISCTL_MS;
-			break;
-		case SND_SOC_DAIFMT_CBS_CFS:   /* codec clk & frm slave */
-			reg_val &= ~SUNXI_IISCTL_MS;
-			break;
-		default:
-			return -EINVAL;
+	//reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
+	if(sunxi_iis.slave)
+	{
+		reg_val |= SUNXI_IISCTL_MS; // 1: Slave!
+		printk("[IIS-0] sunxi_i2s_set_fmt: set slave mode for I2S interface\n");
+	}
+	else
+	{
+		reg_val &= ~SUNXI_IISCTL_MS; // 0: Master!
+		printk("[IIS-0] sunxi_i2s_set_fmt: set master mode for I2S interface\n");
 	}
 	writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
 
+	sunxi_iis.lrc_pol = 0;
 	/* pcm or i2s mode selection */
 	reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
 	reg_val1 = readl(sunxi_iis.regs + SUNXI_IISFAT0);
@@ -255,24 +257,36 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 		case SND_SOC_DAIFMT_I2S:        /* I2S mode */
 			reg_val &= ~SUNXI_IISCTL_PCM;
 			reg_val1 |= SUNXI_IISFAT0_FMT_I2S;
+			sunxi_iis.samp_format = 0;
+			printk("[IIS-0] sunxi_i2s_set_fmt: set I2S mode\n");
 			break;
 		case SND_SOC_DAIFMT_RIGHT_J:    /* Right Justified mode */
 			reg_val &= ~SUNXI_IISCTL_PCM;
 			reg_val1 |= SUNXI_IISFAT0_FMT_RGT;
+			sunxi_iis.samp_format = 2;
+			printk("[IIS-0] sunxi_i2s_set_fmt: set Right Justified mode\n");
 			break;
 		case SND_SOC_DAIFMT_LEFT_J:     /* Left Justified mode */
 			reg_val &= ~SUNXI_IISCTL_PCM;
 			reg_val1 |= SUNXI_IISFAT0_FMT_LFT;
+			sunxi_iis.samp_format = 1;
+			/*printk("[IIS-0] sunxi_i2s_set_fmt: set Left Justified mode\n");*/
 			break;
 		case SND_SOC_DAIFMT_DSP_A:      /* L data msb after FRM LRC */
 			reg_val |= SUNXI_IISCTL_PCM;
 			reg_val1 &= ~SUNXI_IISFAT0_LRCP;
+			sunxi_iis.samp_format = 3;
+			printk("[IIS-0] sunxi_i2s_set_fmt: set L data msb after FRM LRC mode\n");
 			break;
 		case SND_SOC_DAIFMT_DSP_B:      /* L data msb during FRM LRC */
 			reg_val |= SUNXI_IISCTL_PCM;
 			reg_val1 |= SUNXI_IISFAT0_LRCP;
+			sunxi_iis.samp_format = 3;
+			sunxi_iis.lrc_pol = 1;
+			printk("[IIS-0] sunxi_i2s_set_fmt: set L data msb during FRM LRC mode\n");
 			break;
 		default:
+			printk("[IIS-0] sunxi_i2s_set_fmt: unknown mode\n");
 			return -EINVAL;
 	}
 	writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
@@ -284,25 +298,41 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 		case SND_SOC_DAIFMT_NB_NF:     /* normal bit clock + frame */
 			reg_val1 &= ~SUNXI_IISFAT0_LRCP;
 			reg_val1 &= ~SUNXI_IISFAT0_BCP;
+			sunxi_iis.lrc_pol = 0;
+			sunxi_iis.bclk_pol = 0;
+			printk("[IIS-0] sunxi_i2s_set_fmt: normal bit clock + frame\n");
 			break;
 		case SND_SOC_DAIFMT_NB_IF:     /* normal bclk + inv frm */
 			reg_val1 |= SUNXI_IISFAT0_LRCP;
 			reg_val1 &= ~SUNXI_IISFAT0_BCP;
+			sunxi_iis.lrc_pol = 1;
+			sunxi_iis.bclk_pol = 0;
+			printk("[IIS-0] sunxi_i2s_set_fmt: normal bclk + inv frm\n");
 			break;
 		case SND_SOC_DAIFMT_IB_NF:     /* invert bclk + nor frm */
 			reg_val1 &= ~SUNXI_IISFAT0_LRCP;
 			reg_val1 |= SUNXI_IISFAT0_BCP;
+			sunxi_iis.lrc_pol = 0;
+			sunxi_iis.bclk_pol = 1;
+			printk("[IIS-0] sunxi_i2s_set_fmt: invert bclk + nor frm\n");
 			break;
 		case SND_SOC_DAIFMT_IB_IF:     /* invert bclk + frm */
 			reg_val1 |= SUNXI_IISFAT0_LRCP;
 			reg_val1 |= SUNXI_IISFAT0_BCP;
+			sunxi_iis.lrc_pol = 1;
+			sunxi_iis.bclk_pol = 1;
+			printk("[IIS-0] sunxi_i2s_set_fmt: invert bclk + frm\n");
 			break;
 	}
 	writel(reg_val1, sunxi_iis.regs + SUNXI_IISFAT0);
 
-	/* word select size */
+	/* clear word select size */
 	reg_val = readl(sunxi_iis.regs + SUNXI_IISFAT0);
 	reg_val &= ~SUNXI_IISFAT0_WSS_32BCLK;
+	/* word size hardcoded to 32 (ref. sunxi-sndi2s.c func. sunxi_sndi2s_hw_params()) */
+	sunxi_iis.ws_size = 32;
+	printk("[IIS-0] sunxi_i2s_set_fmt: word size = %d\n", sunxi_iis.ws_size);
+	/*
 	if(sunxi_iis.ws_size == 16)
 		reg_val |= SUNXI_IISFAT0_WSS_16BCLK;
 	else if(sunxi_iis.ws_size == 20)
@@ -310,47 +340,57 @@ static int sunxi_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 	else if(sunxi_iis.ws_size == 24)
 		reg_val |= SUNXI_IISFAT0_WSS_24BCLK;
 	else
-		reg_val |= SUNXI_IISFAT0_WSS_32BCLK;
+	*/
+	reg_val |= SUNXI_IISFAT0_WSS_32BCLK;
+	
 	writel(reg_val, sunxi_iis.regs + SUNXI_IISFAT0);
 
 	/* PCM REGISTER setup */
-	reg_val = sunxi_iis.pcm_txtype&0x3;
-	reg_val |= sunxi_iis.pcm_rxtype<<2;
+	reg_val = 0;
+	//reg_val = sunxi_iis.pcm_txtype&0x3;
+	//reg_val |= sunxi_iis.pcm_rxtype<<2;
 
 	if(!sunxi_iis.pcm_sync_type)
+	{
 		reg_val |= SUNXI_IISFAT1_SSYNC;							//short sync
+		printk("[IIS-0] sunxi_i2s_set_fmt: set pcm_sync_type = short sync\n");
+	}
 	if(sunxi_iis.pcm_sw == 16)
+	{
 		reg_val |= SUNXI_IISFAT1_SW;
+		printk("[IIS-0] sunxi_i2s_set_fmt: pcm_sw == 16\n");
+	}
 
 	reg_val |=((sunxi_iis.pcm_start_slot - 1)&0x3)<<6;		//start slot index
 
 	reg_val |= sunxi_iis.pcm_lsb_first<<9;			//MSB or LSB first
 
 	if(sunxi_iis.pcm_sync_period == 256)
-		reg_val |= 0x4<<12;
+		reg_val |= SUNXI_IISFAT1_SYNCLEN_256BCLK;
 	else if (sunxi_iis.pcm_sync_period == 128)
-		reg_val |= 0x3<<12;
+		reg_val |= SUNXI_IISFAT1_SYNCLEN_128BCLK;
 	else if (sunxi_iis.pcm_sync_period == 64)
-		reg_val |= 0x2<<12;
+		reg_val |= SUNXI_IISFAT1_SYNCLEN_64BCLK;
 	else if (sunxi_iis.pcm_sync_period == 32)
-		reg_val |= 0x1<<12;
+		reg_val |= SUNXI_IISFAT1_SYNCLEN_32BCLK;
 	writel(reg_val, sunxi_iis.regs + SUNXI_IISFAT1);
 
 	/* set FIFO control register */
-	reg_val = 0 & 0x3;
-	reg_val |= (1 & 0x1)<<2;
-	reg_val |= SUNXI_IISFCTL_RXTL(0xf);				//RX FIFO trigger level
-	reg_val |= SUNXI_IISFCTL_TXTL(0x40);				//TX FIFO empty trigger level
+	reg_val = 0;
+	reg_val |= SUNXI_IISFCTL_RXTL(0xf);			//RX FIFO trigger level
+	reg_val |= SUNXI_IISFCTL_TXTL(0x40);			//TX FIFO empty trigger level
 	writel(reg_val, sunxi_iis.regs + SUNXI_IISFCTL);
 	return 0;
 }
 
 static int sunxi_i2s_hw_params(struct snd_pcm_substream *substream,
-																struct snd_pcm_hw_params *params,
-																struct snd_soc_dai *dai)
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
 {
+	u32 reg_val;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct sunxi_dma_params *dma_data;
+	printk("[IIS-0] sunxi_i2s_hw_params: %s\n", substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "recording");
 
 	/* play or record */
 	if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -358,7 +398,48 @@ static int sunxi_i2s_hw_params(struct snd_pcm_substream *substream,
 	else
 		dma_data = &sunxi_i2s_pcm_stereo_in;
 
+	/* set format info */
+	reg_val = readl(sunxi_iis.regs + SUNXI_IISFAT0);
+	/* clear sample resolution select size */
+	reg_val &= ~SUNXI_IISFAT0_SR_RVD;
+
+	switch (params_format(params)) 
+	{
+	case SNDRV_PCM_FORMAT_S16_LE:
+		reg_val |= SUNXI_IISFAT0_SR_16BIT;
+		sunxi_iis.samp_res = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		reg_val |= SUNXI_IISFAT0_SR_20BIT;
+		sunxi_iis.samp_res = 20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		reg_val |= SUNXI_IISFAT0_SR_24BIT;
+		sunxi_iis.samp_res = 24;
+		break;
+	default:
+		pr_err("[IIS-0] sunxi_i2s_hw_params: Unsupported format (%d)\n", (int)params_format(params));
+		return -EINVAL;
+	}
+	writel(reg_val, sunxi_iis.regs + SUNXI_IISFAT0);
+
+	/* set FIFO control register */
+	reg_val = readl(sunxi_iis.regs + SUNXI_IISFCTL);
+	reg_val |= SUNXI_IISFCTL_TXIM_MOD1;				//1: Valid data at the LSB of TXFIFO register
+	//CHECK EXPANDING FORMAT!!!
+	if(sunxi_iis.samp_res == 24) {
+		reg_val &= ~SUNXI_IISFCTL_RXOM_MOD3;			//00: Expanding 0 at LSB of DA_RXFIFO register
+	}
+	else {
+		reg_val |= SUNXI_IISFCTL_RXOM_MOD1;			//00: Expanding 0 at LSB of DA_RXFIFO register
+	}
+	writel(reg_val, sunxi_iis.regs + SUNXI_IISFCTL);
+
 	snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
+	sunxi_iis.samp_fs = params_rate(params);
+	sunxi_iis.channel_num =  params_channels(params);
+	printk("[IIS-0] sunxi_i2s_hw_params: channel num %d, format %d bit, sample rate %d\n", 
+		sunxi_iis.channel_num, sunxi_iis.samp_res, sunxi_iis.samp_fs);
 	return 0;
 }
 
@@ -375,7 +456,7 @@ static int sunxi_i2s_trigger(struct snd_pcm_substream *substream,
 		case SNDRV_PCM_TRIGGER_RESUME:
 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 			if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-				sunxi_snd_rxctrl_i2s(1);
+				sunxi_snd_rxctrl_i2s(substream, 1);
 			} else {
 				sunxi_snd_txctrl_i2s(substream, 1);
 			}
@@ -385,7 +466,7 @@ static int sunxi_i2s_trigger(struct snd_pcm_substream *substream,
 		case SNDRV_PCM_TRIGGER_SUSPEND:
 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 			if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-				sunxi_snd_rxctrl_i2s(0);
+				sunxi_snd_rxctrl_i2s(substream, 0);
 			} else {
 			  sunxi_snd_txctrl_i2s(substream, 0);
 			}
@@ -403,9 +484,17 @@ static int sunxi_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
                                  unsigned int freq, int dir)
 {
 	if (!freq) {
-		clk_set_rate(i2s_pll2clk, 24576000);
+		/*printk("[IIS-0] sunxi_i2s_set_sysclk: set sysclk=24576000\n");*/
+		if(sunxi_iis.slave)
+			gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_sel");
+		else
+			clk_set_rate(i2s_pll2clk, 24576000);
 	} else {
-		clk_set_rate(i2s_pll2clk, 22579200);
+		/*printk("[IIS-0] sunxi_i2s_set_sysclk: set sysclk=22579200\n");*/
+		if(sunxi_iis.slave)
+			gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_sel");
+		else
+			clk_set_rate(i2s_pll2clk, 22579200);
 	}
 
 	return 0;
@@ -414,6 +503,27 @@ static int sunxi_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
 static int sunxi_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div)
 {
 	u32 reg;
+	/*printk("[IIS-0] sunxi_i2s_set_clkdiv: PLL clock div_id=(%s), div=(%d)\n", 
+		div_id == SUNXI_DIV_MCLK ? "SUNXI_DIV_MCLK" : 
+		(div_id == SUNXI_DIV_BCLK ? "SUNXI_DIV_BCLK" : "SUNXI_DIV_EXTCLK"), div);*/
+
+
+	if(sunxi_iis.slave) {
+		if(div_id != SUNXI_DIV_EXTCLK) {
+			pr_err("[I2S-0] try to set external clock divider failed\n");
+			return -EINVAL;
+		}
+		/*printk("[IIS-0] sunxi_i2s_set_clkdiv: external clock, div=(%d)\n", div);*/
+	}
+	else {
+		if(div_id != SUNXI_DIV_MCLK && div_id != SUNXI_DIV_BCLK) {
+			pr_err("[I2S-0] try to set PLL clock divider failed\n");
+			return -EINVAL;
+		}
+		/*printk("[IIS-0] sunxi_i2s_set_clkdiv: PLL clock div_id=(%s), div=(%d)\n", 
+			div_id == SUNXI_DIV_MCLK ? "SUNXI_DIV_MCLK" : "SUNXI_DIV_BCLK", div);*/
+	}
+
 	switch (div_id) {
 		case SUNXI_DIV_MCLK:
 			if(div <= 8)
@@ -447,18 +557,43 @@ static int sunxi_i2s_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div
 			reg = (readl(sunxi_iis.regs + SUNXI_IISCLKD) & ~SUNXI_IISCLKD_BCLK_MASK) | (div <<SUNXI_IISCLKD_BCLK_OFFS);
 			writel(reg, sunxi_iis.regs + SUNXI_IISCLKD);
 			break;
+		case SUNXI_DIV_EXTCLK:
+			/*printk("[IIS-0] sunxi_i2s_set_clkdiv set divider=(%d)\n", div);*/
+			if(div == 512) {
+				gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_div1");
+				gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_div0");
+			}
+			else if(div == 256) {
+				gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div1");
+				gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_div0");
+			}
+			else if(div == 128) {
+				gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div1");
+				gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div0");
+			}
+			else if(div == 64) {
+				gpio_write_one_pin_value(i2s_handle, 1, "i2s_clk_div1");
+				gpio_write_one_pin_value(i2s_handle, 0, "i2s_clk_div0");
+			}
+			else {
+				pr_err("[I2S-0] try to set unsupported external clock divider div=(%d)\n", div);
+				return -EINVAL;
+			}
+			break;
 		default:
 			return -EINVAL;
 	}
 
-	//diable MCLK output when high samplerate
+	//disable MCLK output when high samplerate or slave mode
 	reg = readl(sunxi_iis.regs + SUNXI_IISCLKD);
-	if (!(reg & 0xF)) {
+	if (!(reg & 0xF) || sunxi_iis.slave) {
 		reg &= ~SUNXI_IISCLKD_MCLKOEN;
 		writel(reg, sunxi_iis.regs + SUNXI_IISCLKD);
+		/*printk("[IIS-0] sunxi_i2s_set_clkdiv: disable MCLK\n");*/
 	} else {
 		reg |= SUNXI_IISCLKD_MCLKOEN;
 		writel(reg, sunxi_iis.regs + SUNXI_IISCLKD);
+		/*printk("[IIS-0] sunxi_i2s_set_clkdiv: enable MCLK\n");*/
 	}
 
 	return 0;
@@ -483,6 +618,8 @@ static void iisregsave(void)
 	regsave[5] = readl(sunxi_iis.regs + SUNXI_IISCLKD);
 	regsave[6] = readl(sunxi_iis.regs + SUNXI_TXCHSEL);
 	regsave[7] = readl(sunxi_iis.regs + SUNXI_TXCHMAP);
+	regsave[8] = readl(sunxi_iis.regs + SUNXI_RXCHSEL);
+	regsave[9] = readl(sunxi_iis.regs + SUNXI_RXCHMAP);
 }
 
 static void iisregrestore(void)
@@ -495,57 +632,71 @@ static void iisregrestore(void)
 	writel(regsave[5], sunxi_iis.regs + SUNXI_IISCLKD);
 	writel(regsave[6], sunxi_iis.regs + SUNXI_TXCHSEL);
 	writel(regsave[7], sunxi_iis.regs + SUNXI_TXCHMAP);
+	writel(regsave[8], sunxi_iis.regs + SUNXI_RXCHSEL);
+	writel(regsave[9], sunxi_iis.regs + SUNXI_RXCHMAP);
 }
 
 static int sunxi_i2s_suspend(struct snd_soc_dai *cpu_dai)
 {
 	u32 reg_val;
-	printk("[IIS]Entered %s\n", __func__);
+	printk("[I2S-0] Entered %s\n", __func__);
 
-	//Global Enable Digital Audio Interface
+	//Global Disable Digital Audio Interface
 	reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
 	reg_val &= ~SUNXI_IISCTL_GEN;
 	writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
 
 	iisregsave();
 
-	//release the module clock
-	clk_disable(i2s_moduleclk);
-
+	if(!sunxi_iis.slave) {
+		//release the module clock, only for master mode
+		clk_disable(i2s_moduleclk);
+	}
 	clk_disable(i2s_apbclk);
 
 	//printk("[IIS]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008);
-	printk("[IIS]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__);
-	printk("[IIS]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__);
+	printk("[I2S-0] SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__);
+	printk("[I2S-0] SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__);
 
 	return 0;
 }
 static int sunxi_i2s_resume(struct snd_soc_dai *cpu_dai)
 {
 	u32 reg_val;
-	printk("[IIS]Entered %s\n", __func__);
+	printk("[I2S-0] Entered %s\n", __func__);
 
-	//release the module clock
+	//enable the module clock
 	clk_enable(i2s_apbclk);
 
-	//release the module clock
-	clk_enable(i2s_moduleclk);
+	if(!sunxi_iis.slave) {
+
+		//enable the module clock
+		clk_enable(i2s_moduleclk);
+	}
 
 	iisregrestore();
 
 	//Global Enable Digital Audio Interface
 	reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
+	if(sunxi_iis.slave)
+		reg_val |= SUNXI_IISCTL_MS; // 1: Slave!
+	else
+		reg_val &= ~SUNXI_IISCTL_MS; // 0: Master!
 	reg_val |= SUNXI_IISCTL_GEN;
 	writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
 
 	//printk("[IIS]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008);
-	printk("[IIS]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__);
-	printk("[IIS]SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__);
+	printk("[I2S-0] SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__);
+	printk("[I2S-0] SPECIAL CLK 0x01c200B8 = %#x, line = %d\n", *(volatile int*)0xF1C200B8, __LINE__);
 
 	return 0;
 }
 
-#define SUNXI_I2S_RATES (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT)
+#define SUNXI_I2S_RATES_MASTER (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT)
+#define SUNXI_I2S_RATES_SLAVE (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+				SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |\
+				SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000 |\
+				SNDRV_PCM_RATE_352800 | SNDRV_PCM_RATE_384000)
 static struct snd_soc_dai_ops sunxi_iis_dai_ops = {
 	.trigger 	= sunxi_i2s_trigger,
 	.hw_params 	= sunxi_i2s_hw_params,
@@ -562,14 +713,14 @@ static struct snd_soc_dai_driver sunxi_iis_dai = {
 	.playback 	= {
 		.channels_min = 1,
 		.channels_max = 2,
-		.rates = SUNXI_I2S_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+		.rates = SUNXI_I2S_RATES_MASTER,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
 	},
 	.capture 	= {
 		.channels_min = 1,
 		.channels_max = 2,
-		.rates = SUNXI_I2S_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+		.rates = SUNXI_I2S_RATES_MASTER,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
 	},
 	.symmetric_rates = 1,
 	.ops 		= &sunxi_iis_dai_ops,
@@ -579,42 +730,65 @@ static int __devinit sunxi_i2s_dev_probe(struct platform_device *pdev)
 {
 	int reg_val = 0;
 	int ret;
+	printk("[I2S-0] Entered %s\n", __func__);
 
 	sunxi_iis.regs = ioremap(SUNXI_IISBASE, 0x100);
 	if (sunxi_iis.regs == NULL)
 		return -ENXIO;
 
 	//i2s apbclk
-	i2s_apbclk = clk_get(NULL, "apb_i2s");
+	i2s_apbclk = clk_get(NULL, 
+#if defined CONFIG_ARCH_SUN7I
+		"apb_i2s0"
+#else
+		"apb_i2s"
+#endif
+		);
+
 	if(-1 == clk_enable(i2s_apbclk)){
-		printk("i2s_apbclk failed! line = %d\n", __LINE__);
+		pr_err("[I2S-0] i2s_apbclk failed! line = %d\n", __LINE__);
 		goto out;
 	}
 
-	i2s_pllx8 = clk_get(NULL, "audio_pllx8");
-
-	//i2s pll2clk
-	i2s_pll2clk = clk_get(NULL, "audio_pll");
-
-	//i2s module clk
-	i2s_moduleclk = clk_get(NULL, "i2s");
+	if(!sunxi_iis.slave) {
+
+		i2s_pllx8 = clk_get(NULL, "audio_pllx8");
+		//i2s pll2clk
+		i2s_pll2clk = clk_get(NULL, "audio_pll");
+		//i2s module clk
+		i2s_moduleclk = clk_get(NULL, 
+#if defined CONFIG_ARCH_SUN7I
+			"i2s0"
+#else
+			"i2s"
+#endif
+			);
 
-	if(clk_set_parent(i2s_moduleclk, i2s_pll2clk)){
-		printk("try to set parent of i2s_moduleclk to i2s_pll2ck failed! line = %d\n",__LINE__);
-		goto out1;
-	}
+		if(clk_set_parent(i2s_moduleclk, i2s_pll2clk)){
+			pr_err("[I2S-0] try to set parent of i2s_moduleclk to i2s_pll2ck failed! line = %d\n",__LINE__);
+			goto out1;
+		}
 
-	if(clk_set_rate(i2s_moduleclk, 24576000/8)){
-		printk("set i2s_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__);
-		goto out1;
-	}
+		if(clk_set_rate(i2s_moduleclk, 24576000/8)){
+			pr_err("[I2S-0] set i2s_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__);
+			goto out1;
+		}
 
-	if(-1 == clk_enable(i2s_moduleclk)){
-		printk("open i2s_moduleclk failed! line = %d\n", __LINE__);
-		goto out1;
-	}
+		if(-1 == clk_enable(i2s_moduleclk)){
+			pr_err("[I2S-0] open i2s_moduleclk failed! line = %d\n", __LINE__);
+			goto out1;
+		}
+	} else
+		{
+			sunxi_iis_dai.playback.rates = SUNXI_I2S_RATES_SLAVE;
+			sunxi_iis_dai.capture.rates = SUNXI_I2S_RATES_SLAVE;
+		}
 
 	reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
+	if(sunxi_iis.slave)
+		reg_val |= SUNXI_IISCTL_MS; // 1: Slave!
+	else
+		reg_val &= ~SUNXI_IISCTL_MS; // 0: Master!
 	reg_val |= SUNXI_IISCTL_GEN;
 	writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
 
@@ -627,7 +801,8 @@ static int __devinit sunxi_i2s_dev_probe(struct platform_device *pdev)
 
 	goto out;
 	out2:
-		clk_disable(i2s_moduleclk);
+		if(!sunxi_iis.slave)
+			clk_disable(i2s_moduleclk);
 	out1:
 		clk_disable(i2s_apbclk);
 	out:
@@ -636,17 +811,26 @@ static int __devinit sunxi_i2s_dev_probe(struct platform_device *pdev)
 
 static int __devexit sunxi_i2s_dev_remove(struct platform_device *pdev)
 {
+	int reg_val = 0;
+	printk("[I2S-0] Entered %s\n", __func__);
+
 	if(i2s_used) {
-		i2s_used = 0;
-		//release the module clock
-		clk_disable(i2s_moduleclk);
+		//Global disable Digital Audio Interface
+		reg_val = readl(sunxi_iis.regs + SUNXI_IISCTL);
+		reg_val &= ~SUNXI_IISCTL_GEN;
+		writel(reg_val, sunxi_iis.regs + SUNXI_IISCTL);
 
-		//release pllx8clk
-		clk_put(i2s_pllx8);
+		i2s_used = 0;
+		if(!sunxi_iis.slave) {
+			//release the module clock
+			clk_disable(i2s_moduleclk);
 
-		//release pll2clk
-		clk_put(i2s_pll2clk);
+			//release pllx8clk
+			clk_put(i2s_pllx8);
 
+			//release pll2clk
+			clk_put(i2s_pll2clk);
+		}
 		//release apbclk
 		clk_put(i2s_apbclk);
 
@@ -674,15 +858,26 @@ static struct platform_driver sunxi_i2s_driver = {
 
 static int __init sunxi_i2s_init(void)
 {
-	int err = 0;
+	int err = 0, i2s_slave = 0;
 	int ret;
 
+	printk("[I2S-0] Entered %s\n", __func__);
+
 	ret = script_parser_fetch("i2s_para","i2s_used", &i2s_used, sizeof(int));
 	if (ret) {
-        printk("[I2S]sunxi_i2s_init fetch i2s using configuration failed\n");
-    }
+        	printk("[I2S-0] sunxi_i2s_init fetch i2s using configuration failed\n");
+	}
 
  	if (i2s_used) {
+		ret = script_parser_fetch("i2s_para","i2s_slave", &i2s_slave, sizeof(int));
+		if (ret == 0 && i2s_slave) {
+			sunxi_iis.slave = 1;
+			printk("[I2S-0] sunxi_i2s_init I2S used in slave mode\n");
+		} else {
+			sunxi_iis.slave = 0;
+			printk("[I2S-0] sunxi_i2s_init I2S used in master mode\n");
+		}
+
 		i2s_handle = gpio_request_ex("i2s_para", NULL);
 
 		if((err = platform_device_register(&sunxi_i2s_device)) < 0)
@@ -691,7 +886,7 @@ static int __init sunxi_i2s_init(void)
 		if ((err = platform_driver_register(&sunxi_i2s_driver)) < 0)
 			return err;
 	} else {
-        printk("[I2S]sunxi-i2s cannot find any using configuration for controllers, return directly!\n");
+        printk("[I2S-0] sunxi-i2s cannot find any using configuration for controllers, return directly!\n");
         return 0;
     }
 	return 0;
@@ -700,6 +895,7 @@ module_init(sunxi_i2s_init);
 
 static void __exit sunxi_i2s_exit(void)
 {
+	printk("[I2S-0] Entered %s\n", __func__);
 	platform_driver_unregister(&sunxi_i2s_driver);
 }
 module_exit(sunxi_i2s_exit);
diff --git a/sound/soc/sunxi/i2s/sunxi-i2s.h b/sound/soc/sunxi/i2s/sunxi-i2s.h
index f12d6d5..ba6ac32 100644
--- a/sound/soc/sunxi/i2s/sunxi-i2s.h
+++ b/sound/soc/sunxi/i2s/sunxi-i2s.h
@@ -20,71 +20,71 @@
 /* REGISTER definition */
 
 /* IIS REGISTER */
-#define SUNXI_IISBASE 		(0x01C22400)
+#define SUNXI_IISBASE 				(0x01C22400)
 
-#define SUNXI_IISCTL 	  (0x00)
+#define SUNXI_IISCTL 	  			(0x00)
 	#define SUNXI_IISCTL_SDO3EN		(1<<11)
 	#define SUNXI_IISCTL_SDO2EN		(1<<10)
 	#define SUNXI_IISCTL_SDO1EN		(1<<9)
 	#define SUNXI_IISCTL_SDO0EN		(1<<8)
-	#define SUNXI_IISCTL_ASS			(1<<6)
+	#define SUNXI_IISCTL_ASS		(1<<6)
 	#define SUNXI_IISCTL_MS			(1<<5)
-	#define SUNXI_IISCTL_PCM			(1<<4)
-	#define SUNXI_IISCTL_LOOP			(1<<3)
-	#define SUNXI_IISCTL_TXEN			(1<<2)
-	#define SUNXI_IISCTL_RXEN			(1<<1)
-	#define SUNXI_IISCTL_GEN			(1<<0)
-
-#define SUNXI_IISFAT0 		(0x04)
-	#define SUNXI_IISFAT0_LRCP					(1<<7)
-	#define SUNXI_IISFAT0_BCP					(1<<6)
-	#define SUNXI_IISFAT0_SR_RVD				(3<<4)
-	#define SUNXI_IISFAT0_SR_16BIT				(0<<4)
-	#define	SUNXI_IISFAT0_SR_20BIT				(1<<4)
-	#define SUNXI_IISFAT0_SR_24BIT				(2<<4)
-	#define SUNXI_IISFAT0_WSS_16BCLK			(0<<2)
-	#define SUNXI_IISFAT0_WSS_20BCLK			(1<<2)
-	#define SUNXI_IISFAT0_WSS_24BCLK			(2<<2)
-	#define SUNXI_IISFAT0_WSS_32BCLK			(3<<2)
-	#define SUNXI_IISFAT0_FMT_I2S				(0<<0)
-	#define SUNXI_IISFAT0_FMT_LFT				(1<<0)
-	#define SUNXI_IISFAT0_FMT_RGT				(2<<0)
-	#define SUNXI_IISFAT0_FMT_RVD				(3<<0)
-
-#define SUNXI_IISFAT1		(0x08)
-	#define SUNXI_IISFAT1_SYNCLEN_16BCLK		(0<<12)
-	#define SUNXI_IISFAT1_SYNCLEN_32BCLK		(1<<12)
-	#define SUNXI_IISFAT1_SYNCLEN_64BCLK		(2<<12)
-	#define SUNXI_IISFAT1_SYNCLEN_128BCLK		(3<<12)
-	#define SUNXI_IISFAT1_SYNCLEN_256BCLK		(4<<12)
-	#define SUNXI_IISFAT1_SYNCOUTEN			(1<<11)
-	#define SUNXI_IISFAT1_OUTMUTE 				(1<<10)
-	#define SUNXI_IISFAT1_MLS		 			(1<<9)
-	#define SUNXI_IISFAT1_SEXT		 			(1<<8)
-	#define SUNXI_IISFAT1_SI_1ST				(0<<6)
-	#define SUNXI_IISFAT1_SI_2ND			 	(1<<6)
-	#define SUNXI_IISFAT1_SI_3RD			 	(2<<6)
-	#define SUNXI_IISFAT1_SI_4TH			 	(3<<6)
-	#define SUNXI_IISFAT1_SW			 		(1<<5)
-	#define SUNXI_IISFAT1_SSYNC	 			(1<<4)
-	#define SUNXI_IISFAT1_RXPDM_16PCM			(0<<2)
-	#define SUNXI_IISFAT1_RXPDM_8PCM			(1<<2)
-	#define SUNXI_IISFAT1_RXPDM_8ULAW			(2<<2)
-	#define SUNXI_IISFAT1_RXPDM_8ALAW  		(3<<2)
-	#define SUNXI_IISFAT1_TXPDM_16PCM			(0<<0)
-	#define SUNXI_IISFAT1_TXPDM_8PCM			(1<<0)
-	#define SUNXI_IISFAT1_TXPDM_8ULAW			(2<<0)
-	#define SUNXI_IISFAT1_TXPDM_8ALAW  		(3<<0)
-
-#define SUNXI_IISTXFIFO 	(0x0C)
-
-#define SUNXI_IISRXFIFO 	(0x10)
-
-#define SUNXI_IISFCTL  	(0x14)
-	#define SUNXI_IISFCTL_FIFOSRC			(1<<31)
-	#define SUNXI_IISFCTL_FTX				(1<<25)
-	#define SUNXI_IISFCTL_FRX				(1<<24)
-	#define SUNXI_IISFCTL_TXTL(v)			((v)<<12)
+	#define SUNXI_IISCTL_PCM		(1<<4)
+	#define SUNXI_IISCTL_LOOP		(1<<3)
+	#define SUNXI_IISCTL_TXEN		(1<<2)
+	#define SUNXI_IISCTL_RXEN		(1<<1)
+	#define SUNXI_IISCTL_GEN		(1<<0)
+
+#define SUNXI_IISFAT0 				(0x04)
+	#define SUNXI_IISFAT0_LRCP		(1<<7)
+	#define SUNXI_IISFAT0_BCP		(1<<6)
+	#define SUNXI_IISFAT0_SR_RVD		(3<<4)
+	#define SUNXI_IISFAT0_SR_16BIT		(0<<4)
+	#define	SUNXI_IISFAT0_SR_20BIT		(1<<4)
+	#define SUNXI_IISFAT0_SR_24BIT		(2<<4)
+	#define SUNXI_IISFAT0_WSS_16BCLK	(0<<2)
+	#define SUNXI_IISFAT0_WSS_20BCLK	(1<<2)
+	#define SUNXI_IISFAT0_WSS_24BCLK	(2<<2)
+	#define SUNXI_IISFAT0_WSS_32BCLK	(3<<2)
+	#define SUNXI_IISFAT0_FMT_I2S		(0<<0)
+	#define SUNXI_IISFAT0_FMT_LFT		(1<<0)
+	#define SUNXI_IISFAT0_FMT_RGT		(2<<0)
+	#define SUNXI_IISFAT0_FMT_RVD		(3<<0)
+
+#define SUNXI_IISFAT1				(0x08)
+	#define SUNXI_IISFAT1_SYNCLEN_16BCLK	(0<<12)
+	#define SUNXI_IISFAT1_SYNCLEN_32BCLK	(1<<12)
+	#define SUNXI_IISFAT1_SYNCLEN_64BCLK	(2<<12)
+	#define SUNXI_IISFAT1_SYNCLEN_128BCLK	(3<<12)
+	#define SUNXI_IISFAT1_SYNCLEN_256BCLK	(4<<12)
+	#define SUNXI_IISFAT1_SYNCOUTEN		(1<<11)
+	#define SUNXI_IISFAT1_OUTMUTE 		(1<<10)
+	#define SUNXI_IISFAT1_MLS		(1<<9)
+	#define SUNXI_IISFAT1_SEXT		(1<<8)
+	#define SUNXI_IISFAT1_SI_1ST		(0<<6)
+	#define SUNXI_IISFAT1_SI_2ND		(1<<6)
+	#define SUNXI_IISFAT1_SI_3RD		(2<<6)
+	#define SUNXI_IISFAT1_SI_4TH		(3<<6)
+	#define SUNXI_IISFAT1_SW		(1<<5)
+	#define SUNXI_IISFAT1_SSYNC	 	(1<<4)
+	#define SUNXI_IISFAT1_RXPDM_16PCM	(0<<2)
+	#define SUNXI_IISFAT1_RXPDM_8PCM	(1<<2)
+	#define SUNXI_IISFAT1_RXPDM_8ULAW	(2<<2)
+	#define SUNXI_IISFAT1_RXPDM_8ALAW  	(3<<2)
+	#define SUNXI_IISFAT1_TXPDM_16PCM	(0<<0)
+	#define SUNXI_IISFAT1_TXPDM_8PCM	(1<<0)
+	#define SUNXI_IISFAT1_TXPDM_8ULAW	(2<<0)
+	#define SUNXI_IISFAT1_TXPDM_8ALAW  	(3<<0)
+
+#define SUNXI_IISTXFIFO 			(0x0C)
+
+#define SUNXI_IISRXFIFO 			(0x10)
+
+#define SUNXI_IISFCTL  				(0x14)
+	#define SUNXI_IISFCTL_FIFOSRC		(1<<31)
+	#define SUNXI_IISFCTL_FTX		(1<<25)
+	#define SUNXI_IISFCTL_FRX		(1<<24)
+	#define SUNXI_IISFCTL_TXTL(v)		((v)<<12)
 	#define SUNXI_IISFCTL_RXTL(v)  		((v)<<4)
 	#define SUNXI_IISFCTL_TXIM_MOD0		(0<<2)
 	#define SUNXI_IISFCTL_TXIM_MOD1		(1<<2)
@@ -93,174 +93,101 @@
 	#define SUNXI_IISFCTL_RXOM_MOD2		(2<<0)
 	#define SUNXI_IISFCTL_RXOM_MOD3		(3<<0)
 
-#define SUNXI_IISFSTA   	(0x18)
-	#define SUNXI_IISFSTA_TXE				(1<<28)
+#define SUNXI_IISFSTA   			(0x18)
+	#define SUNXI_IISFSTA_TXE		(1<<28)
 	#define SUNXI_IISFSTA_TXECNT(v)		((v)<<16)
-	#define SUNXI_IISFSTA_RXA				(1<<8)
+	#define SUNXI_IISFSTA_RXA		(1<<8)
 	#define SUNXI_IISFSTA_RXACNT(v)		((v)<<0)
 
-#define SUNXI_IISINT    	(0x1C)
-	#define SUNXI_IISINT_TXDRQEN				(1<<7)
-	#define SUNXI_IISINT_TXUIEN				(1<<6)
-	#define SUNXI_IISINT_TXOIEN				(1<<5)
-	#define SUNXI_IISINT_TXEIEN				(1<<4)
-	#define SUNXI_IISINT_RXDRQEN				(1<<2)
-	#define SUNXI_IISINT_RXOIEN				(1<<1)
-	#define SUNXI_IISINT_RXAIEN				(1<<0)
-
-#define SUNXI_IISISTA   	(0x20)
-	#define SUNXI_IISISTA_TXUISTA			(1<<6)
-	#define SUNXI_IISISTA_TXOISTA			(1<<5)
-	#define SUNXI_IISISTA_TXEISTA			(1<<4)
-	#define SUNXI_IISISTA_RXOISTA			(1<<1)
-	#define SUNXI_IISISTA_RXAISTA			(1<<0)
-
-#define SUNXI_IISCLKD   	(0x24)
-	#define SUNXI_IISCLKD_MCLKOEN			(1<<7)
+#define SUNXI_IISINT    			(0x1C)
+	#define SUNXI_IISINT_TXDRQEN		(1<<7)
+	#define SUNXI_IISINT_TXUIEN		(1<<6)
+	#define SUNXI_IISINT_TXOIEN		(1<<5)
+	#define SUNXI_IISINT_TXEIEN		(1<<4)
+	#define SUNXI_IISINT_RXDRQEN		(1<<3)
+	#define SUNXI_IISINT_RXUIEN		(1<<2)
+	#define SUNXI_IISINT_RXOIEN		(1<<1)
+	#define SUNXI_IISINT_RXAIEN		(1<<0)
+
+#define SUNXI_IISISTA   			(0x20)
+	#define SUNXI_IISISTA_TXUISTA		(1<<6)
+	#define SUNXI_IISISTA_TXOISTA		(1<<5)
+	#define SUNXI_IISISTA_TXEISTA		(1<<4)
+	#define SUNXI_IISISTA_RXUISTA		(1<<2)
+	#define SUNXI_IISISTA_RXOISTA		(1<<1)
+	#define SUNXI_IISISTA_RXAISTA		(1<<0)
+
+#define SUNXI_IISCLKD   			(0x24)
+	#define SUNXI_IISCLKD_MCLKOEN		(1<<7)
 	#define SUNXI_IISCLKD_BCLKDIV_2		(0<<4)
 	#define SUNXI_IISCLKD_BCLKDIV_4		(1<<4)
 	#define SUNXI_IISCLKD_BCLKDIV_6		(2<<4)
 	#define SUNXI_IISCLKD_BCLKDIV_8		(3<<4)
-	#define SUNXI_IISCLKD_BCLKDIV_12		(4<<4)
-	#define SUNXI_IISCLKD_BCLKDIV_16		(5<<4)
-	#define SUNXI_IISCLKD_BCLKDIV_32		(6<<4)
-	#define SUNXI_IISCLKD_BCLKDIV_64		(7<<4)
+	#define SUNXI_IISCLKD_BCLKDIV_12	(4<<4)
+	#define SUNXI_IISCLKD_BCLKDIV_16	(5<<4)
+	#define SUNXI_IISCLKD_BCLKDIV_32	(6<<4)
+	#define SUNXI_IISCLKD_BCLKDIV_64	(7<<4)
 	#define SUNXI_IISCLKD_MCLKDIV_1		(0<<0)
 	#define SUNXI_IISCLKD_MCLKDIV_2		(1<<0)
 	#define SUNXI_IISCLKD_MCLKDIV_4		(2<<0)
 	#define SUNXI_IISCLKD_MCLKDIV_6		(3<<0)
 	#define SUNXI_IISCLKD_MCLKDIV_8		(4<<0)
-	#define SUNXI_IISCLKD_MCLKDIV_12		(5<<0)
-	#define SUNXI_IISCLKD_MCLKDIV_16		(6<<0)
-	#define SUNXI_IISCLKD_MCLKDIV_24		(7<<0)
-	#define SUNXI_IISCLKD_MCLKDIV_32		(8<<0)
-	#define SUNXI_IISCLKD_MCLKDIV_48		(9<<0)
-	#define SUNXI_IISCLKD_MCLKDIV_64		(10<<0)
-
-#define SUNXI_IISTXCNT  	(0x28)
-
-#define SUNXI_IISRXCNT  	(0x2C)
-
-#define SUNXI_TXCHSEL		(0x30)
-	#define SUNXI_TXCHSEL_CHNUM(v)			(((v)-1)<<0)
-
-#define SUNXI_TXCHMAP		(0x34)
-	#define SUNXI_TXCHMAP_CH7(v)			(((v)-1)<<28)
-	#define SUNXI_TXCHMAP_CH6(v)			(((v)-1)<<24)
-	#define SUNXI_TXCHMAP_CH5(v)			(((v)-1)<<20)
-	#define SUNXI_TXCHMAP_CH4(v)			(((v)-1)<<16)
-	#define SUNXI_TXCHMAP_CH3(v)			(((v)-1)<<12)
-	#define SUNXI_TXCHMAP_CH2(v)			(((v)-1)<<8)
-	#define SUNXI_TXCHMAP_CH1(v)			(((v)-1)<<4)
-	#define SUNXI_TXCHMAP_CH0(v)			(((v)-1)<<0)
-
-#define SUNXI_RXCHSEL		(0x38)
-	#define SUNXI_RXCHSEL_CHNUM(v)			(((v)-1)<<0)
-
-#define SUNXI_RXCHMAP		(0x3C)
-	#define SUNXI_RXCHMAP_CH3(v)			(((v)-1)<<12)
-	#define SUNXI_RXCHMAP_CH2(v)			(((v)-1)<<8)
-	#define SUNXI_RXCHMAP_CH1(v)			(((v)-1)<<4)
-	#define SUNXI_RXCHMAP_CH0(v)			(((v)-1)<<0)
-
-
-/* DMA REGISTER */
-#define SUNXI_DMABASE	(0x01C02000)
-
-#define SUNXI_DMAIRQEN						(0x0)
-	#define SUNXI_DMAIRQEN_NDMA_FULLEN(v)				(1<<((v)*2+1))
-	#define SUNXI_DMAIRQEN_NDMA_HALFEN(v)				(1<<((v)*2))
-
-#define SUNXI_DMAIRQPENDING	 		(0x4)
-	#define SUNXI_DMAIRQPENGDING_NDMA_FULLPEND(v)		(1<<((v)*2+1))
-	#define SUNXI_DMAIRQPENGDING_NDMA_HALFPEND(v)		(1<<((v)*2))
-
-#define SUNXI_NDMACFG(v)				((v)*0x20+0x100)
-	#define SUNXI_NDMACFG_DMALOAD					(1<<31)
-	#define SUNXI_NDMACFG_BUSY						(1<<30)
-	#define SUNXI_NDMACFG_CONTINUOUS				(1<<29)
-	#define SUNXI_NDMACFG_WAIT(v)					(((v)-1)<<26)   //wait clock = 2^n  example: 8 clocks = 2^3
-	#define SUNXI_NDMACFG_DSTDATAWIDTH_8BIT		(0<<24)
-	#define SUNXI_NDMACFG_DSTDATAWIDTH_16BIT		(1<<24)
-	#define SUNXI_NDMACFG_DSTDATAWIDTH_32BIT		(2<<24)
-	#define SUNXI_NDMACFG_DSTDATAWIDTH_RVD			(3<<24)
-	#define SUNXI_NDMACFG_DSTBURST4				(1<<23)
-	#define SUNXI_NDMACFG_DSTADDRTYPE_INC			(0<<21)
-	#define SUNXI_NDMACFG_DSTADDRTYPE_CON 			(1<<21)
-	#define SUNXI_NDMACFG_DSTTYPE_IRTX				(0x0<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_SPDIFTX			(0x1<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_IISTX			(0x2<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_AC97TX			(0x3<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_SPI0TX 			(0x4<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_SPI1TX			(0x5<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_SPI2TX			(0x6<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_UART0TX			(0x8<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_UART1TX			(0x9<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_UART2TX			(0xA<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_UART3TX			(0xB<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_AUDIODA			(0xC<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_NFC				(0xF<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_SRAM				(0x10<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_DRAM				(0x11<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_UART4TX			(0x12<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_UART5TX          (0x13<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_UART6TX			(0x14<<16)
-	#define SUNXI_NDMACFG_DSTTYPE_UART7TX			(0x15<<16)
-	#define SUNXI_NDMACFG_SRCDATAWIDTH_8BIT		(0<<8)
-	#define SUNXI_NDMACFG_SRCDATAWIDTH_16BIT		(1<<8)
-	#define SUNXI_NDMACFG_SRCDATAWIDTH_32BIT		(2<<8)
-	#define SUNXI_NDMACFG_SRCDATAWIDTH_RVD			(3<<8)
-	#define SUNXI_NDMACFG_SRCBURST4				(1<<7)
-	#define SUNXI_NDMACFG_SRCADDRTYPE_INC			(0<<5)
-	#define SUNXI_NDMACFG_SRCADDRTYPE_CON 			(1<<5)
-	#define SUNXI_NDMACFG_SRCTYPE_IRRX				(0x0<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_SPDIFRX			(0x1<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_IISRX			(0x2<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_AC97RX			(0x3<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_SPI0RX 			(0x4<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_SPI1RX			(0x5<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_SPI2RX			(0x6<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_UART0RX			(0x8<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_UART1RX			(0x9<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_UART2RX			(0xA<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_UART3RX			(0xB<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_AUDIOAD			(0xC<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_TPAD				(0xD<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_NFC				(0xF<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_SRAM				(0x10<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_DRAM				(0x11<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_UART4RX			(0x12<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_UART5RX			(0x13<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_UART6RX			(0x14<<0)
-	#define SUNXI_NDMACFG_SRCTYPE_UART7RX			(0x15<<0)
-
-#define SUNXI_NDMASRCADDR(v)				((v)*0x20 + 0x100 + 4)
-
-#define SUNXI_NDMADSTADDR(v)				((v)*0x20 + 0x100 + 8)
-
-#define SUNXI_NDMACNT(v)				((v)*0x20 + 0x100 + 0xC)
+	#define SUNXI_IISCLKD_MCLKDIV_12	(5<<0)
+	#define SUNXI_IISCLKD_MCLKDIV_16	(6<<0)
+	#define SUNXI_IISCLKD_MCLKDIV_24	(7<<0)
+	#define SUNXI_IISCLKD_MCLKDIV_32	(8<<0)
+	#define SUNXI_IISCLKD_MCLKDIV_48	(9<<0)
+	#define SUNXI_IISCLKD_MCLKDIV_64	(10<<0)
+
+#define SUNXI_IISTXCNT  			(0x28)
+
+#define SUNXI_IISRXCNT  			(0x2C)
+
+#define SUNXI_TXCHSEL				(0x30)
+	#define SUNXI_TXCHSEL_CHNUM(v)		(((v)-1)<<0)
+
+#define SUNXI_TXCHMAP				(0x34)
+	#define SUNXI_TXCHMAP_CH7(v)		(((v)-1)<<28)
+	#define SUNXI_TXCHMAP_CH6(v)		(((v)-1)<<24)
+	#define SUNXI_TXCHMAP_CH5(v)		(((v)-1)<<20)
+	#define SUNXI_TXCHMAP_CH4(v)		(((v)-1)<<16)
+	#define SUNXI_TXCHMAP_CH3(v)		(((v)-1)<<12)
+	#define SUNXI_TXCHMAP_CH2(v)		(((v)-1)<<8)
+	#define SUNXI_TXCHMAP_CH1(v)		(((v)-1)<<4)
+	#define SUNXI_TXCHMAP_CH0(v)		(((v)-1)<<0)
+
+#define SUNXI_RXCHSEL				(0x38)
+	#define SUNXI_RXCHSEL_CHNUM(v)		(((v)-1)<<0)
+
+#define SUNXI_RXCHMAP				(0x3C)
+	#define SUNXI_RXCHMAP_CH3(v)		(((v)-1)<<12)
+	#define SUNXI_RXCHMAP_CH2(v)		(((v)-1)<<8)
+	#define SUNXI_RXCHMAP_CH1(v)		(((v)-1)<<4)
+	#define SUNXI_RXCHMAP_CH0(v)		(((v)-1)<<0)
+
 
 
 /* CCM REGISTER */
-#define SUNXI_CCMBASE    (0x01C20000)
+#define SUNXI_CCMBASE    			(0x01C20000)
 
-#define SUNXI_CCM_AUDIO_HOSC_PLL_REG   (0x08)
+#define SUNXI_CCM_AUDIO_HOSC_PLL_REG   		(0x08)
 	#define SUNXI_CCM_AUDIO_HOSC_PLL_REG_AUDIOEN		(1<<31)
 	#define SUNXI_CCM_AUDIO_HOSC_PLL_REG_FRE225792MHZ	(0<<27)
 	#define SUNXI_CCM_AUDIO_HOSC_PLL_REG_FRE24576MHZ	(1<<27)
 
-#define SUNXI_CCM_APB_GATE_REG    		 (0x68)
-	#define SUNXI_CCM_APB_GATE_REG_IISGATE				(1<<3)
+#define SUNXI_CCM_APB_GATE_REG    		(0x68)
+	#define SUNXI_CCM_APB_GATE_REG_IISGATE	(1<<3)
 
-#define SUNXI_CCM_AUDIO_CLK_REG				(0xb8)
+#define SUNXI_CCM_AUDIO_CLK_REG			(0xb8)
 	#define SUNXI_CCM_AUDIO_CLK_REG_IISSPECIALGATE		(1<<31)
-	#define SUNXI_CCM_AUDIO_CLK_REG_DIV(v)					((v)<<16)
+	#define SUNXI_CCM_AUDIO_CLK_REG_DIV(v)			((v)<<16)
 /*------------------------------------------------------------*/
 
 /*------------------------------------------------------------*/
 /* Clock dividers */
-#define SUNXI_DIV_MCLK	0
-#define SUNXI_DIV_BCLK	1
+#define SUNXI_DIV_MCLK		0
+#define SUNXI_DIV_BCLK		1
+#define SUNXI_DIV_EXTCLK	2
 
 #define SUNXI_IISCLKD_MCLK_MASK   0x0f
 #define SUNXI_IISCLKD_MCLK_OFFS   0
@@ -269,36 +196,34 @@
 #define SUNXI_IISCLKD_MCLKEN_OFFS 7
 
 unsigned int sunxi_i2s_get_clockrate(void);
-extern struct sunxi_i2s_info sunxi_i2s;
-//extern struct snd_soc_dai sunxi_iis_dai;
 
 extern void sunxi_snd_txctrl_i2s(struct snd_pcm_substream *substream, int on);
-extern void sunxi_snd_rxctrl_i2s(int on);
+extern void sunxi_snd_rxctrl_i2s(struct snd_pcm_substream *substream, int on);
 
 struct sunxi_i2s_info {
-	void __iomem   *regs;    /* IIS BASE */
-	void __iomem   *ccmregs;  //CCM BASE
-	void __iomem   *ioregs;   //IO BASE
+	void __iomem   *regs;    	//IIS BASE
+	void __iomem   *ccmregs;  	//CCM BASE
+	void __iomem   *ioregs;   	//IO BASE
 
-	u32 slave;					//0: master, 1: slave
-	u32 mono;					//0: stereo, 1: mono
-	u32 samp_fs;				//audio sample rate (unit in kHz)
+	u32 slave;			//0: master, 1: slave
+	u32 channel_num;		//
+	u32 samp_fs;			//audio sample rate (unit in kHz)
 	u32 samp_res;			//16 bits, 20 bits , 24 bits, 32 bits)
 	u32 samp_format;		//audio sample format (0: standard I2S, 1: left-justified, 2: right-justified, 3: pcm)
-	u32 ws_size;				//16 BCLK, 20 BCLK, 24 BCLK, 32 BCLK)
+	u32 ws_size;			//16 BCLK, 20 BCLK, 24 BCLK, 32 BCLK)
 	u32 mclk_rate;			//mclk frequency divide by fs (128fs, 192fs, 256fs, 384fs, 512fs, 768fs)
-	u32 lrc_pol;				//LRC clock polarity (0: normal ,1: inverted)
+	u32 lrc_pol;			//LRC clock polarity (0: normal ,1: inverted)
 	u32 bclk_pol;			//BCLK polarity (0: normal, 1: inverted)
-	u32 pcm_txtype;		//PCM transmitter type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law)
-	u32 pcm_rxtype;		//PCM receiver type  (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law)
-	u32 pcm_sw;				//PCM slot width (8: 8 bits, 16: 16 bits)
-	u32 pcm_sync_period;//PCM sync period (16/32/64/128/256)
-	u32 pcm_sync_type;	//PCM sync symbol size (0: short sync, 1: long sync)
-	u32 pcm_start_slot;//PCM start slot index (1--4)
-	u32 pcm_lsb_first;	//0: MSB first, 1: LSB first
-	u32 pcm_ch_num;		//PCM channel number (1: one channel, 2: two channel)
+	u32 pcm_txtype;			//PCM transmitter type (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law)
+	u32 pcm_rxtype;			//PCM receiver type  (0: 16-bits linear mode, 1: 8-bits linear mode, 2: u-law, 3: A-law)
+	u32 pcm_sw;			//PCM slot width (8: 8 bits, 16: 16 bits)
+	u32 pcm_sync_period;		//PCM sync period (16/32/64/128/256)
+	u32 pcm_sync_type;		//PCM sync symbol size (0: short sync, 1: long sync)
+	u32 pcm_start_slot;		//PCM start slot index (1--4)
+	u32 pcm_lsb_first;		//0: MSB first, 1: LSB first
+	u32 pcm_ch_num;			//PCM channel number (1: one channel, 2: two channel)
 
 };
 
-extern struct sunxi_i2s_info sunxi_i2s;
+//extern struct sunxi_i2s_info sunxi_iis;
 #endif
diff --git a/sound/soc/sunxi/i2s/sunxi-i2sdma.c b/sound/soc/sunxi/i2s/sunxi-i2sdma.c
index 488ef4d..57f82c1 100644
--- a/sound/soc/sunxi/i2s/sunxi-i2sdma.c
+++ b/sound/soc/sunxi/i2s/sunxi-i2sdma.c
@@ -32,17 +32,32 @@
 #include "sunxi-i2s.h"
 #include "sunxi-i2sdma.h"
 
-static volatile unsigned int dmasrc = 0;
-static volatile unsigned int dmadst = 0;
+static const struct snd_pcm_hardware sunxi_pcm_out_hardware = {
+	.info			= SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				      SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+				      SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+	.rates			= SNDRV_PCM_RATE_8000_384000 | SNDRV_PCM_RATE_KNOT,
+	.rate_min		= 8000,
+	.rate_max		= 384000,
+	.channels_min		= 1,
+	.channels_max		= 2,
+	.buffer_bytes_max	= 128*1024,    /* value must be (2^n)Kbyte size */
+	.period_bytes_min	= 1024*4,//1024*4,
+	.period_bytes_max	= 1024*32,//1024*32,
+	.periods_min		= 4,//4,
+	.periods_max		= 8,//8,
+	.fifo_size		= 128,
+};
 
-static const struct snd_pcm_hardware sunxi_pcm_hardware = {
+static const struct snd_pcm_hardware sunxi_pcm_in_hardware = {
 	.info			= SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				      SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
 				      SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
-	.formats		= SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
-	.rates			= SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+	.rates			= SNDRV_PCM_RATE_8000_384000 | SNDRV_PCM_RATE_KNOT,
 	.rate_min		= 8000,
-	.rate_max		= 192000,
+	.rate_max		= 384000,
 	.channels_min		= 1,
 	.channels_max		= 2,
 	.buffer_bytes_max	= 128*1024,    /* value must be (2^n)Kbyte size */
@@ -50,7 +65,7 @@ static const struct snd_pcm_hardware sunxi_pcm_hardware = {
 	.period_bytes_max	= 1024*32,//1024*32,
 	.periods_min		= 4,//4,
 	.periods_max		= 8,//8,
-	.fifo_size		= 128,//32,
+	.fifo_size		= 64,
 };
 
 struct sunxi_runtime_data {
@@ -63,32 +78,32 @@ struct sunxi_runtime_data {
 	dma_addr_t dma_pos;
 	dma_addr_t dma_end;
 	struct sunxi_dma_params *params;
+	/*DMA data width*/
+	unsigned int dma_width;
 };
 
 static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream)
 {
+	int ret = 0;
 	struct sunxi_runtime_data *prtd = substream->runtime->private_data;
 	dma_addr_t pos = prtd->dma_pos;
-	unsigned int limit;
-	int ret;
-
 	unsigned long len = prtd->dma_period;
-  	limit = prtd->dma_limit;
-  	while(prtd->dma_loaded < limit) {
-		if((pos + len) > prtd->dma_end) {
+	unsigned int limit = prtd->dma_limit;
+	int read = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1;
+
+	while(prtd->dma_loaded < limit){
+		if((pos + len) > prtd->dma_end){
 			len  = prtd->dma_end - pos;
 		}
-
-		ret = sunxi_dma_enqueue(prtd->params, pos,  len, 0);
-		if(ret == 0) {
+		ret = sunxi_dma_enqueue(prtd->params, pos, len, read);
+		if(ret == 0){
 			prtd->dma_loaded++;
 			pos += prtd->dma_period;
 			if(pos >= prtd->dma_end)
 				pos = prtd->dma_start;
-		}else {
+		}else{
 			break;
 		}
-
 	}
 	prtd->dma_pos = pos;
 }
@@ -125,17 +140,32 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream,
 	if (!dma)
 		return 0;
 
+	prtd->dma_width = 16;
+	/* set DMA width for using in sunxi_pcm_prepare*/
+	switch(params_format(params)) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+		prtd->dma_width = 16;
+		break;
+		case SNDRV_PCM_FORMAT_S20_3LE:
+		prtd->dma_width = 32;
+		break;
+		case SNDRV_PCM_FORMAT_S24_LE:
+		prtd->dma_width = 32;
+		break;
+	}
 	if (prtd->params == NULL) {
 		prtd->params = dma;
 		ret = sunxi_dma_request(prtd->params, 0);
 		if (ret < 0) {
-				return ret;
+			printk("[IIS-0] sunxi_dma_request failed! ret == %d\n", ret);
+			return ret;
 		}
 	}
 
 	if (sunxi_dma_set_callback(prtd->params, sunxi_audio_buffdone,
 							    substream) != 0) {
 		sunxi_dma_release(prtd->params);
+		printk("[IIS-0] sunxi_dma_set_callback failed! ret == %d\n", ret);
 		prtd->params = NULL;
 		return -EINVAL;
 	}
@@ -151,6 +181,7 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream,
 	prtd->dma_start = runtime->dma_addr;
 	prtd->dma_pos = prtd->dma_start;
 	prtd->dma_end = prtd->dma_start + totbytes;
+
 	spin_unlock_irq(&prtd->lock);
 	return 0;
 }
@@ -196,24 +227,62 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream)
 #else
 		dma_config_t codec_dma_conf;
 		memset(&codec_dma_conf, 0, sizeof(codec_dma_conf));
-		codec_dma_conf.xfer_type.src_data_width	= DATA_WIDTH_16BIT;
-		codec_dma_conf.xfer_type.src_bst_len	= DATA_BRST_1;	
-		codec_dma_conf.xfer_type.dst_data_width	= DATA_WIDTH_16BIT;
-		codec_dma_conf.xfer_type.dst_bst_len	= DATA_BRST_1;
+
+		printk("[IIS-0] sunxi_pcm_prepare: playback DMA data width=(%d)\n", prtd->dma_width);
+		if(prtd->dma_width > 16)
+		{
+			codec_dma_conf.xfer_type.src_data_width	= DATA_WIDTH_32BIT;
+			codec_dma_conf.xfer_type.dst_data_width	= DATA_WIDTH_32BIT;
+		}
+		else
+		{
+			codec_dma_conf.xfer_type.src_data_width	= DATA_WIDTH_16BIT;
+			codec_dma_conf.xfer_type.dst_data_width	= DATA_WIDTH_16BIT;
+		}
+		codec_dma_conf.xfer_type.src_bst_len	= DATA_BRST_4;
+		codec_dma_conf.xfer_type.dst_bst_len	= DATA_BRST_4;
 		codec_dma_conf.address_type.src_addr_mode = NDMA_ADDR_INCREMENT;
 		codec_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_NOCHANGE;
 		codec_dma_conf.src_drq_type		= N_SRC_SDRAM;
 		codec_dma_conf.dst_drq_type		= N_DST_IIS0_TX;
 		codec_dma_conf.bconti_mode		= false;
-		codec_dma_conf.irq_spt			= CHAN_IRQ_FD;
+		codec_dma_conf.irq_spt			= CHAN_IRQ_FD; //buf full done irq
+#endif
+		ret = sunxi_dma_config(prtd->params, &codec_dma_conf, 0);
+	}
+	else {
+#if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I
+#else
+		dma_config_t codec_dma_conf;
+		memset(&codec_dma_conf, 0, sizeof(codec_dma_conf));
+
+		printk("[IIS-0] sunxi_pcm_prepare: capture DMA data width=(%d)\n", prtd->dma_width);
+		if(prtd->dma_width > 16)
+		{
+			codec_dma_conf.xfer_type.src_data_width	= DATA_WIDTH_32BIT;
+			codec_dma_conf.xfer_type.dst_data_width	= DATA_WIDTH_32BIT;
+		}
+		else
+		{
+			codec_dma_conf.xfer_type.src_data_width	= DATA_WIDTH_16BIT;
+			codec_dma_conf.xfer_type.dst_data_width	= DATA_WIDTH_16BIT;
+		}
+		codec_dma_conf.xfer_type.src_bst_len	= DATA_BRST_4;	
+		codec_dma_conf.xfer_type.dst_bst_len	= DATA_BRST_4;
+		codec_dma_conf.address_type.src_addr_mode = NDMA_ADDR_NOCHANGE;
+		codec_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_INCREMENT;
+		codec_dma_conf.src_drq_type		= N_SRC_IIS0_RX;
+		codec_dma_conf.dst_drq_type		= N_DST_SDRAM;
+		codec_dma_conf.bconti_mode		= false;
+		codec_dma_conf.irq_spt			= CHAN_IRQ_FD; //buf full done irq
 #endif
 		ret = sunxi_dma_config(prtd->params, &codec_dma_conf, 0);
 	}
 
 	/* flush the DMA channel */
 	prtd->dma_loaded = 0;
-	if (sunxi_dma_flush(prtd->params) == 0)
-		prtd->dma_pos = prtd->dma_start;
+	sunxi_dma_flush(prtd->params);
+	prtd->dma_pos = prtd->dma_start;
 
 	/* enqueue dma buffers */
 	sunxi_pcm_enqueue(substream);
@@ -231,15 +300,16 @@ static int sunxi_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		printk("[IIS] dma trigger start\n");
-		printk("[IIS] 0x01c22400+0x24 = %#x, line= %d\n", readl(0xf1c22400+0x24), __LINE__);
+		printk("[IIS-0] dma trigger start\n");
+		prtd->state |= ST_RUNNING;
 		sunxi_dma_start(prtd->params);
 		break;
 
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-        printk("[IIS] dma trigger stop\n");
+        printk("[IIS-0] dma trigger stop\n");
+		prtd->state &= ~ST_RUNNING;
 		sunxi_dma_stop(prtd->params);
 		break;
 
@@ -258,31 +328,42 @@ static snd_pcm_uframes_t sunxi_pcm_pointer(struct snd_pcm_substream *substream)
 	struct sunxi_runtime_data *prtd = runtime->private_data;
 	unsigned long res = 0;
 	snd_pcm_uframes_t offset = 0;
+	unsigned int dmasrc = 0;
+	unsigned int dmadst = 0;
 
 	spin_lock(&prtd->lock);
 	sunxi_dma_getcurposition(prtd->params,
-				 (dma_addr_t*)&dmasrc, (dma_addr_t*)&dmadst);
+			 (dma_addr_t*)&dmasrc, (dma_addr_t*)&dmadst);
 
-	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-		res = dmadst - prtd->dma_start;
-	else
-	{
-		offset = bytes_to_frames(runtime, dmasrc + prtd->dma_period - runtime->dma_addr);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		res = dmasrc + prtd->dma_period - prtd->dma_start;
+	}
+	else {
+	    	res = dmadst + prtd->dma_period - prtd->dma_start;
+		//res = dmadst - prtd->dma_start;
 	}
+	offset = bytes_to_frames(runtime, res);
 	spin_unlock(&prtd->lock);
 
 	if(offset >= runtime->buffer_size)
 		offset = 0;
-		return offset;
+
+	return offset;
 }
 
+
 static int sunxi_pcm_open(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct sunxi_runtime_data *prtd;
 
 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-	snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_hardware);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_out_hardware);
+	}
+	else {
+		snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_in_hardware);
+	}
 
 	prtd = kzalloc(sizeof(struct sunxi_runtime_data), GFP_KERNEL);
 	if (prtd == NULL)
@@ -331,13 +412,19 @@ static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 {
 	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 	struct snd_dma_buffer *buf = &substream->dma_buffer;
-	size_t size = sunxi_pcm_hardware.buffer_bytes_max;
-
+	size_t size = 0;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		size = sunxi_pcm_out_hardware.buffer_bytes_max;
+	}
+	else {
+		size = sunxi_pcm_in_hardware.buffer_bytes_max;
+	}
 	buf->dev.type = SNDRV_DMA_TYPE_DEV;
 	buf->dev.dev = pcm->card->dev;
 	buf->private_data = NULL;
 	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
 					   &buf->addr, GFP_KERNEL);
+
 	if (!buf->area)
 		return -ENOMEM;
 	buf->bytes = size;
diff --git a/sound/soc/sunxi/i2s/sunxi-i2sdma.h b/sound/soc/sunxi/i2s/sunxi-i2sdma.h
index 50418a5..8f0b029 100644
--- a/sound/soc/sunxi/i2s/sunxi-i2sdma.h
+++ b/sound/soc/sunxi/i2s/sunxi-i2sdma.h
@@ -29,7 +29,7 @@ enum sunxi_dma_buffresult {
 };
 
 /* platform data */
-extern struct snd_soc_platform sunxi_soc_platform_i2s;
-extern struct sunxi_i2s_info sunxi_iis;
+//extern struct snd_soc_platform sunxi_soc_platform_i2s;
+//extern struct sunxi_i2s_info sunxi_iis;
 
 #endif //SUNXI_PCM_H_
diff --git a/sound/soc/sunxi/i2s/sunxi-sndi2s.c b/sound/soc/sunxi/i2s/sunxi-sndi2s.c
index 7c1a3d4..ab10c86 100644
--- a/sound/soc/sunxi/i2s/sunxi-sndi2s.c
+++ b/sound/soc/sunxi/i2s/sunxi-sndi2s.c
@@ -29,9 +29,8 @@
 
 #include "sndi2s.h"
 
-static struct clk *xtal;
-static int clk_users;
-static DEFINE_MUTEX(clk_lock);
+/* slave mode flag*/
+static int sunxi_i2s_slave = 0;
 
 #ifdef ENFORCE_RATES
 static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
@@ -45,7 +44,7 @@ static int sunxi_sndi2s_startup(struct snd_pcm_substream *substream)
 {
 	int ret = 0;
 	#ifdef ENFORCE_RATES
-		struct snd_pcm_runtime *runtime = substream->runtime;;
+		struct snd_pcm_runtime *runtime = substream->runtime;
 	#endif
 
 	if (!ret) {
@@ -62,14 +61,6 @@ static int sunxi_sndi2s_startup(struct snd_pcm_substream *substream)
 
 static void sunxi_sndi2s_shutdown(struct snd_pcm_substream *substream)
 {
-	mutex_lock(&clk_lock);
-	clk_users -= 1;
-	if (clk_users == 0) {
-		clk_put(xtal);
-		xtal = NULL;
-
-	}
-	mutex_unlock(&clk_lock);
 }
 
 typedef struct __MCLK_SET_INF
@@ -91,6 +82,14 @@ typedef struct __BCLK_SET_INF
 
 } __bclk_set_inf;
 
+typedef struct __EXTCLK_SET_INF
+{
+    __u32       samp_rate;      // sample rate
+    __u16       clk_div;        // masterclock division
+    __u16       mpll;           // select mpll, 0 - 24.576 Mhz, 1 - 22.5792 Mhz
+
+} __extclk_set_inf;
+
 
 static __bclk_set_inf BCLK_INF[] =
 {
@@ -164,7 +163,50 @@ static __mclk_set_inf  MCLK_INF[] =
     {0xffffffff, 0, 0, 0},
 };
 
-static s32 get_clock_divder(u32 sample_rate, u32 sample_width, u32 * mclk_div, u32* mpll, u32* bclk_div, u32* mult_fs)
+static __extclk_set_inf  EXTCLK_INF[] =
+{
+    //44.1k bitrate
+    { 44100, 512,  1},
+    //48k bitrate
+    { 48000, 512,  0},
+    //88.2k bitrate
+    { 88200, 256,  1},
+    //96k bitrate
+    { 96000, 256,  0},
+    //176.4k bitrate
+    { 176400, 128,  1},
+    //192k bitrate
+    { 192000, 128,  0},
+
+    //352.8k bitrate
+    { 352800, 64,  1},
+    //384 bitrate
+    { 384000, 64,  0},
+
+    //end flag 0xffffffff
+    {0xffffffff, 0, 0}
+};
+
+
+static s32 get_clock_divder_slave(u32 sample_rate, u32 sample_width, u32* bclk_div, u32* mpll, u32* mult_fs)
+{
+	u32 i, ret = -EINVAL;
+
+	for(i=0; i< 100; i++) {
+		if(EXTCLK_INF[i].samp_rate == sample_rate) {
+			//set mpll and bclk division
+			*mpll = EXTCLK_INF[i].mpll;
+			*bclk_div = EXTCLK_INF[i].clk_div;
+			ret = 0;
+			break;
+		}
+		else if(EXTCLK_INF[i].samp_rate == 0xffffffff)
+			break;
+	}
+	return ret;
+}
+
+static s32 get_clock_divder_master(u32 sample_rate, u32 sample_width, u32 * mclk_div, u32* mpll, u32* bclk_div, u32* mult_fs)
 {
 	u32 i, j, ret = -EINVAL;
 
@@ -201,34 +243,56 @@ static int sunxi_sndi2s_hw_params(struct snd_pcm_substream *substream,
 	unsigned long rate = params_rate(params);
 	u32 mclk_div=0, mpll=0, bclk_div=0, mult_fs=0;
 
-	get_clock_divder(rate, 32, &mclk_div, &mpll, &bclk_div, &mult_fs);
+	if(!sunxi_i2s_slave) {
+		get_clock_divder_master(rate, /*fixed sample width*/32, &mclk_div, &mpll, &bclk_div, &mult_fs);
+		printk("[IIS-0] get_clock_divder_master: rate=(%lu), mclk_div=(%d), mpll=(%d), bclk_div=(%d), mult_fs=(%d)\n", 
+			rate, mclk_div, mpll, bclk_div, mult_fs);
+	} else {
+		get_clock_divder_slave(rate, /*fixed sample width*/32, &bclk_div, &mpll, &mult_fs);
+		printk("[IIS-0] get_clock_divder_slave: rate=(%lu), bclk_div=(%d), mpll=(%d), mult_fs=(%d)\n",
+			rate, bclk_div, mpll, mult_fs);
+	}
 
+	//call sunxi_iis_set_fmt
 	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+			SND_SOC_DAIFMT_NB_NF/* | SND_SOC_DAIFMT_CBM_CFM*/);
 	if (ret < 0)
 		return ret;
 
+	//call sunxi_iis_set_fmt
 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
-			SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+			SND_SOC_DAIFMT_NB_NF/* | SND_SOC_DAIFMT_CBM_CFM*/);
 	if (ret < 0)
 		return ret;
 
+	//call sunxi_iis_set_sysclk
 	ret = snd_soc_dai_set_sysclk(cpu_dai, 0 , mpll, 0);
 	if (ret < 0)
 		return ret;
 
+	//call sndi2s_set_dai_sysclk
 	ret = snd_soc_dai_set_sysclk(codec_dai, 0 , mpll, 0);
 	if (ret < 0)
 		return ret;
 
-	ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_MCLK, mclk_div);
-	if (ret < 0)
-		return ret;
+	if(!sunxi_i2s_slave) {
+		//call sunxi_iis_set_clkdiv	
+		ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_MCLK, mclk_div);
+		if (ret < 0)
+			return ret;
 
-	ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_BCLK, bclk_div);
-	if (ret < 0)
-		return ret;
+		//call sunxi_iis_set_clkdiv	
+		ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_BCLK, bclk_div);
+		if (ret < 0)
+			return ret;
+	} else {
+		//call sunxi_iis_set_clkdiv	
+		ret = snd_soc_dai_set_clkdiv(cpu_dai, SUNXI_DIV_EXTCLK, bclk_div);
+		if (ret < 0)
+			return ret;
+	}
 
+	//call sndi2s_set_dai_clkdiv
 	ret = snd_soc_dai_set_clkdiv(codec_dai, 0, mult_fs);
 	if (ret < 0)
 		return ret;
@@ -243,7 +307,7 @@ static struct snd_soc_ops sunxi_sndi2s_ops = {
 };
 
 static struct snd_soc_dai_link sunxi_sndi2s_dai_link = {
-	.name 			= "I2S",
+	.name 		= "I2S",
 	.stream_name 	= "SUNXI-I2S",
 	.cpu_dai_name 	= "sunxi-i2s.0",
 	.codec_dai_name = "sndi2s",
@@ -286,12 +350,26 @@ static struct platform_driver sunxi_sndi2s_driver = {
 
 static int __init sunxi_sndi2s_init(void)
 {
-	int ret, i2s_used = 0;
+	int ret, i2s_used = 0, i2s_slave = 0;
+
+	printk("[I2S-0] Entered %s\n", __func__);
 
 	ret = script_parser_fetch("i2s_para", "i2s_used", &i2s_used, 1);
 	if (ret != 0 || !i2s_used)
 		return -ENODEV;
 
+	script_parser_fetch("i2s_para","i2s_slave", &i2s_slave, sizeof(int));
+	if (i2s_slave)
+	{
+		sunxi_i2s_slave = 1;
+		printk("[I2S-0] sunxi_sndi2s_init I2S used in slave mode\n");
+	}
+	else
+	{
+		sunxi_i2s_slave = 0;
+		printk("[I2S-0] sunxi_sndi2s_init I2S used in master mode\n");
+	}
+
 	ret = platform_device_register(&sunxi_sndi2s_device);
 	if (ret < 0)
 		return ret;
diff --git a/sound/soc/sunxi/i2s/sunxi-sndi2s.h b/sound/soc/sunxi/i2s/sunxi-sndi2s.h
index 47f0961..89cc71a 100644
--- a/sound/soc/sunxi/i2s/sunxi-sndi2s.h
+++ b/sound/soc/sunxi/i2s/sunxi-sndi2s.h
@@ -14,7 +14,7 @@
  */
 #ifndef SUNXI_SNDI2S_H_
 #define SUNXI_SNDI2S_H_
-
+/* cleaning code
 struct sunxi_sndi2s_platform_data {
 	int iis_bclk;
 	int iis_ws;
@@ -22,4 +22,5 @@ struct sunxi_sndi2s_platform_data {
 	void (*power)(int);
 	int model;
 }
+*/
 #endif
diff --git a/sound/soc/sunxi/spdif/sndspdif.c b/sound/soc/sunxi/spdif/sndspdif.c
index 73e4a95..667e5bb 100644
--- a/sound/soc/sunxi/spdif/sndspdif.c
+++ b/sound/soc/sunxi/spdif/sndspdif.c
@@ -26,7 +26,7 @@
 #include "sndspdif.h"
 
 #define SNDSPDIF_RATES  (SNDRV_PCM_RATE_8000_192000|SNDRV_PCM_RATE_KNOT)
-#define SNDSPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+#define SNDSPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
 struct sndspdif_priv {
 	int sysclk;
@@ -94,9 +94,18 @@ struct snd_soc_dai_driver sndspdif_dai = {
 		.rates = SNDSPDIF_RATES,
 		.formats = SNDSPDIF_FORMATS,
 	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDSPDIF_RATES,
+		.formats = SNDSPDIF_FORMATS,
+	},
 	/* pcm operations */
 	.ops = &sndspdif_dai_ops,
+	//clear start?
 	.symmetric_rates = 1,
+	//clear end?
 };
 EXPORT_SYMBOL(sndspdif_dai);
 
@@ -106,7 +115,7 @@ static int sndspdif_soc_probe(struct snd_soc_codec *codec)
 
 	sndspdif = kzalloc(sizeof(struct sndspdif_priv), GFP_KERNEL);
 	if(sndspdif == NULL){
-		printk("%s,%d\n",__func__,__LINE__);
+		pr_err("[SPDIF] try to alloc sndspdif failed %s,%d\n", __func__,__LINE__);
 		return -ENOMEM;
 	}
 	snd_soc_codec_set_drvdata(codec, sndspdif);
@@ -161,22 +170,27 @@ static int __init sndspdif_codec_init(void)
 	int ret, spdif_used = 0;
 
 	ret = script_parser_fetch("spdif_para", "spdif_used", &spdif_used, 1);
-	if (ret != 0 || !spdif_used)
+	if (ret != 0 || !spdif_used) {
+		printk("[SPDIF] [spdif_para] isn't defined or spdif_used=0\n");
 		return -ENODEV;
+	}
 
 	spd_gpio_hdle = gpio_request_ex("spdif_para", "spdif_dout");
 	if (0 == spd_gpio_hdle) {
-		pr_err("try to request spdif_para gpio failed\n");
+		pr_err("[SPDIF] try to request spdif_para gpio failed %s,%d\n", __func__,__LINE__);
 		return -1;
 	}
 
 	ret = platform_device_register(&sndspdif_codec_device);
-	if (ret < 0)
+	if (ret < 0) {
+		pr_err("[SPDIF] try to SPDIF platform_device_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__);
 		return ret;
+	}
 
 	ret = platform_driver_register(&sndspdif_codec_driver);
 	if (ret < 0) {
 		platform_device_unregister(&sndspdif_codec_device);
+		pr_err("[SPDIF] try to SPDIF platform_driver_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__);
 		return ret;
 	}
 
diff --git a/sound/soc/sunxi/spdif/sunxi_sndspdif.c b/sound/soc/sunxi/spdif/sunxi_sndspdif.c
index 885e7ae..f088e0f 100644
--- a/sound/soc/sunxi/spdif/sunxi_sndspdif.c
+++ b/sound/soc/sunxi/spdif/sunxi_sndspdif.c
@@ -45,7 +45,7 @@ static int sunxi_sndspdif_startup(struct snd_pcm_substream *substream)
 {
 	int ret = 0;
 	#ifdef ENFORCE_RATES
-		struct snd_pcm_runtime *runtime = substream->runtime;;
+		struct snd_pcm_runtime *runtime = substream->runtime;
 	#endif
 	if (!ret) {
 	#ifdef ENFORCE_RATES
@@ -71,7 +71,7 @@ static void sunxi_sndspdif_shutdown(struct snd_pcm_substream *substream)
 typedef struct __MCLK_SET_INF
 {
     __u32   samp_rate;      // sample rate
-	__u16 	mult_fs;        // multiply of smaple rate
+    __u16 	mult_fs;    // multiply of smaple rate
 
     __u8    clk_div;        // mpll division
     __u8    mpll;           // select mpll, 0 - 24.576 Mhz, 1 - 22.5792 Mhz
@@ -83,7 +83,7 @@ typedef struct __BCLK_SET_INF
 {
     __u8    bitpersamp;     // bits per sample
     __u8    clk_div;        // clock division
-    __u16   mult_fs;        // multiplay of sample rate
+    __u16   mult_fs;        // multiply of sample rate
 
 } __bclk_set_inf;
 
@@ -218,13 +218,13 @@ static struct snd_soc_ops sunxi_sndspdif_ops = {
 };
 
 static struct snd_soc_dai_link sunxi_sndspdif_dai_link = {
-	.name 			= "SPDIF",
+	.name 		= "SPDIF",
 	.stream_name 	= "SUNXI-SPDIF",
 	.cpu_dai_name 	= "sunxi-spdif.0",
 	.codec_dai_name = "sndspdif",
 	.platform_name 	= "sunxi-spdif-pcm-audio.0",
 	.codec_name 	= "sunxi-spdif-codec.0",
-	.ops 			= &sunxi_sndspdif_ops,
+	.ops 		= &sunxi_sndspdif_ops,
 };
 
 static struct snd_soc_card snd_soc_sunxi_sndspdif = {
@@ -236,6 +236,12 @@ static struct snd_soc_card snd_soc_sunxi_sndspdif = {
 
 static int __devinit sunxi_sndspdif_probe(struct platform_device *pdev)
 {
+	int ret, spdif_used = 0;
+
+	ret = script_parser_fetch("spdif_para", "spdif_used", &spdif_used, 1);
+	if (ret != 0 || !spdif_used)
+		return -ENODEV;
+
 	snd_soc_sunxi_sndspdif.dev = &pdev->dev;
 	return snd_soc_register_card(&snd_soc_sunxi_sndspdif);
 }
diff --git a/sound/soc/sunxi/spdif/sunxi_spdif.c b/sound/soc/sunxi/spdif/sunxi_spdif.c
index 88389ae..47efb02 100644
--- a/sound/soc/sunxi/spdif/sunxi_spdif.c
+++ b/sound/soc/sunxi/spdif/sunxi_spdif.c
@@ -37,7 +37,7 @@
 #include "sunxi_spdma.h"
 #include "sunxi_spdif.h"
 
-static int regsave[6];
+static int regsave[9];
 
 static struct sunxi_dma_params sunxi_spdif_stereo_out = {
 	.client.name	=	"SPDIF out",
@@ -48,9 +48,9 @@ static struct sunxi_dma_params sunxi_spdif_stereo_out = {
 };
 
 static struct sunxi_dma_params sunxi_spdif_stereo_in = {
-	.client.name	=	"SPDIF out",
+	.client.name	=	"SPDIF in",
 #if defined CONFIG_ARCH_SUN4I || defined CONFIG_ARCH_SUN5I
-	.channel	=	DMACH_NSPDIF,
+	.channel	=	DMACH_NSPDIF, //???
 #endif
 	.dma_addr 	=	SUNXI_SPDIFBASE + SUNXI_SPDIF_RXFIFO,
 };
@@ -63,34 +63,33 @@ void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on)
 {
 	u32 reg_val;
 
+	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
 	if (substream->runtime->channels == 1) {
-		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
 		reg_val |= SUNXI_SPDIF_TXCFG_SINGLEMOD;
-		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
 	}
+	else {
+		reg_val &= ~SUNXI_SPDIF_TXCFG_SINGLEMOD;
+	}
+	reg_val |= SUNXI_SPDIF_TXCFG_ASS;	//Sending the last audio (may be 0?)
+	reg_val |= SUNXI_SPDIF_TXCFG_CHSTMODE;	//Channel status A&B generated form TX_CHSTA
+	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
 
-	//soft reset SPDIF
-	writel(0x1, sunxi_spdif.regs + SUNXI_SPDIF_CTL);
-
-	//MCLK OUTPUT enable
-	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL);
-	reg_val |= SUNXI_SPDIF_CTL_MCLKOUTEN;
-	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL);
-
-	//flush TX FIFO
+	/*flush TX FIFO and set FIFO empty trigger level*/
 	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
-	reg_val |= SUNXI_SPDIF_FCTL_FTX;
+	reg_val |= SUNXI_SPDIF_FCTL_TXTL(0x10);	//TX FIFO empty Trigger Level
+	reg_val |= SUNXI_SPDIF_FCTL_FTX;	//flush TX FIFO
 	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
 
-	//clear interrupt status
+	/*clear interrupt status*/
 	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_ISTA);
+	reg_val |= SUNXI_SPDIF_ISTA_TXCLR;
 	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_ISTA);
 
-	//clear TX counter
+	/*clear TX counter*/
 	writel(0, sunxi_spdif.regs + SUNXI_SPDIF_TXCNT);
 
 	if (on) {
-		//SPDIF TX ENBALE
+		//SPDIF TX ENABLE
 		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
 		reg_val |= SUNXI_SPDIF_TXCFG_TXEN;
 		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
@@ -99,13 +98,8 @@ void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on)
 		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT);
 		reg_val |= SUNXI_SPDIF_INT_TXDRQEN;
 		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT);
-
-		//global enable
-		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL);
-		reg_val |= SUNXI_SPDIF_CTL_GEN;
-		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL);
 	} else {
-		//SPDIF TX DISABALE
+		//SPDIF TX DISABLE
 		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
 		reg_val &= ~SUNXI_SPDIF_TXCFG_TXEN;
 		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
@@ -114,16 +108,48 @@ void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on)
 		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT);
 		reg_val &= ~SUNXI_SPDIF_INT_TXDRQEN;
 		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT);
-
-		//global disable
-		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL);
-		reg_val &= ~SUNXI_SPDIF_CTL_GEN;
-		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL);
 	}
 }
 
-void sunxi_snd_rxctrl(int on)
+void sunxi_snd_rxctrl(struct snd_pcm_substream *substream, int on)
 {
+	u32 reg_val;
+
+	/*flush RX FIFO and set FIFO empty trigger level*/
+	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
+	reg_val |= SUNXI_SPDIF_FCTL_RXTL(0x0F);	//RX FIFO Trigger Level
+	reg_val |= SUNXI_SPDIF_FCTL_FRX;	//flush RX FIFO	
+	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
+	
+	/*clear interrupt status*/
+	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_ISTA);
+	reg_val |= SUNXI_SPDIF_ISTA_RXCLR;
+	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_ISTA);
+	
+	/*clear RX counter*/
+	writel(0, sunxi_spdif.regs + SUNXI_SPDIF_RXCNT);
+
+	if (on) {
+		/*SPDIF RX ENABLE*/
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCFG);
+		reg_val |= SUNXI_SPDIF_RXCFG_RXEN;	
+		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCFG);
+		
+		/*DRQ ENABLE*/
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT);
+		reg_val |= SUNXI_SPDIF_INT_RXDRQEN;	
+		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT);
+	} else {
+		/*SPDIF TX DISABLE*/
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCFG);
+		reg_val &= ~SUNXI_SPDIF_RXCFG_RXEN;	
+		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCFG);
+		
+		/*DRQ DISABLE*/
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT);
+		reg_val &= ~SUNXI_SPDIF_INT_RXDRQEN;	
+		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_INT);
+	}
 }
 
 static inline int sunxi_snd_is_clkmaster(void)
@@ -135,63 +161,122 @@ static int sunxi_spdif_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 {
 	u32 reg_val;
 
-	reg_val = 0;
-	reg_val &= ~SUNXI_SPDIF_TXCFG_SINGLEMOD;
-	reg_val |= SUNXI_SPDIF_TXCFG_ASS;
-	reg_val &= ~SUNXI_SPDIF_TXCFG_NONAUDIO;
-	reg_val |= SUNXI_SPDIF_TXCFG_FMT16BIT;
-	reg_val |= SUNXI_SPDIF_TXCFG_CHSTMODE;
-	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
-
-	reg_val = 0;
-	reg_val &= ~SUNXI_SPDIF_FCTL_FIFOSRC;
-	reg_val |= SUNXI_SPDIF_FCTL_TXTL(16);
-	reg_val |= SUNXI_SPDIF_FCTL_RXTL(15);
-	reg_val |= SUNXI_SPDIF_FCTL_TXIM(1);
-	reg_val |= SUNXI_SPDIF_FCTL_RXOM(3);
-	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
-
 	if (!fmt) {//PCM
-		reg_val = 0;
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
+		reg_val &= ~SUNXI_SPDIF_TXCFG_NONAUDIO;
+		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
+
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
 		reg_val |= (SUNXI_SPDIF_TXCHSTA0_CHNUM(2));
+		reg_val &= ~SUNXI_SPDIF_TXCHSTA0_AUDIO;
 		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
 
-		reg_val = 0;
-		reg_val |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1));
-		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0);
+		reg_val |= (SUNXI_SPDIF_RXCHSTA0_CHNUM(2));
+		reg_val &= ~SUNXI_SPDIF_RXCHSTA0_AUDIO;
+		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0);
 	} else {  //non PCM
 		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
 		reg_val |= SUNXI_SPDIF_TXCFG_NONAUDIO;
 		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
 
-		reg_val = 0;
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
 		reg_val |= (SUNXI_SPDIF_TXCHSTA0_CHNUM(2));
 		reg_val |= SUNXI_SPDIF_TXCHSTA0_AUDIO;
 		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
 
-		reg_val = 0;
-		reg_val |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1));
-		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0);
+		reg_val |= (SUNXI_SPDIF_RXCHSTA0_CHNUM(2));
+		reg_val |= SUNXI_SPDIF_RXCHSTA0_AUDIO;
+		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0);
 	}
 
 	return 0;
 }
 
 static int sunxi_spdif_hw_params(struct snd_pcm_substream *substream,
-																struct snd_pcm_hw_params *params,
-																struct snd_soc_dai *dai)
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct sunxi_dma_params *dma_data;
+	u32 reg_val, reg_val1;
+	int format;
+	switch (params_format(params))
+	{
+	case SNDRV_PCM_FORMAT_S16_LE:
+		format = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		format = 20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		format = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		format = 24;
+		break;
+	default:
+		return -EINVAL;
+	}
 
-	/* play or record */
-	if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+	/* playback or capture */
+	if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		dma_data = &sunxi_spdif_stereo_out;
-	else
+
+		reg_val1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
+		reg_val &= ~SUNXI_SPDIF_TXCFG_FMTRVD;
+		reg_val1 &= ~SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN;
+		reg_val1 &= ~(SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(7));
+		if(format == 16) {
+			reg_val |= SUNXI_SPDIF_TXCFG_FMT16BIT;
+			reg_val1 |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1));
+		}
+		else if(format == 20) {
+			reg_val |= SUNXI_SPDIF_TXCFG_FMT20BIT;
+			reg_val1 |= SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN;
+			reg_val1 |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(1));
+		}
+		else {
+			reg_val |= SUNXI_SPDIF_TXCFG_FMT24BIT;
+			reg_val1 |= SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN;
+			reg_val1 |= (SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(5));
+		}
+		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
+		writel(reg_val1, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+
+		/* Set TX FIFO Input Mode */
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
+		reg_val |= SUNXI_SPDIF_FCTL_TXIM1;	//1. Valid data at the LSB of OWA_TXFIFO register
+		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
+	}
+	else {
 		dma_data = &sunxi_spdif_stereo_in;
+		/* Set TX FIFO Input Mode */
+		reg_val1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1);
+		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
+		reg_val &= ~SUNXI_SPDIF_FCTL_RXOM3;
+		reg_val1 &= ~SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN;
+		reg_val1 &= ~(SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(7));
+
+		if(format == 16) {
+			reg_val |= SUNXI_SPDIF_FCTL_RXOM3;
+			reg_val1 |= (SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(1));
+		}
+		else if(format == 20) {
+			reg_val1 |= SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN;
+			reg_val1 |= (SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(1));
+		}
+		else {
+			reg_val1 |= SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN;
+			reg_val1 |= (SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(5));
+		}
 
+		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
+		writel(reg_val1, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1);
+	}
 	snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_data);
-
 	return 0;
 }
 
@@ -208,7 +293,7 @@ static int sunxi_spdif_trigger(struct snd_pcm_substream *substream,
 		case SNDRV_PCM_TRIGGER_RESUME:
 		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 			if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-				sunxi_snd_rxctrl(1);
+				sunxi_snd_rxctrl(substream, 1);
 			} else {
 				sunxi_snd_txctrl(substream, 1);
 			}
@@ -218,9 +303,9 @@ static int sunxi_spdif_trigger(struct snd_pcm_substream *substream,
 		case SNDRV_PCM_TRIGGER_SUSPEND:
 		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 			if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-				sunxi_snd_rxctrl(0);
+				sunxi_snd_rxctrl(substream, 0);
 			} else {
-			  sunxi_snd_txctrl(substream, 0);
+				sunxi_snd_txctrl(substream, 0);
 			}
 			break;
 		default:
@@ -246,23 +331,31 @@ static int sunxi_spdif_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
 
 static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int div)
 {
-	u32 reg_val = 0;
-
-	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-	reg_val &= ~(SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xf));
-	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-	reg_val &= ~(SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xf));
-  	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+	u32 reg_val_txchsta0 = 0;
+	u32 reg_val_txchsta1 = 0;
+	u32 reg_val_rxchsta0 = 0;
+	u32 reg_val_rxchsta1 = 0;
+	u32 reg_val_txcfg = 0;
 
 	switch(div_id) {
 		case SUNXI_DIV_MCLK:
 		{
-			reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
-			reg_val &= ~SUNXI_SPDIF_TXCFG_TXRATIO(0x1F);
-			reg_val |= SUNXI_SPDIF_TXCFG_TXRATIO(div-1);
-			writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
+			reg_val_txchsta0 = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
+			reg_val_txchsta0 &= ~(SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xf));
+
+			reg_val_txchsta1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+			reg_val_txchsta1 &= ~(SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xf));
+
+			reg_val_rxchsta0 = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0);
+			reg_val_rxchsta0 &= ~(SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xf));
+
+			reg_val_rxchsta1 = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1);
+			reg_val_rxchsta1 &= ~(SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xf));
+
+			reg_val_txcfg = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
+			reg_val_txcfg &= ~SUNXI_SPDIF_TXCFG_TXRATIO(0x1F);
+			reg_val_txcfg |= SUNXI_SPDIF_TXCFG_TXRATIO(div-1);
+			writel(reg_val_txcfg, sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
 
 			if(clk_get_rate(spdif_pll2clk) == 24576000)
 			{
@@ -270,67 +363,49 @@ static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int d
 				{
 					//24KHZ
 					case 8:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x6));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x9));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x6));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x9));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x6));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x9));
 						break;
 
 					//32KHZ
 					case 6:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x3));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xC));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x3));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xC));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x3));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xC));
 						break;
 
 					//48KHZ
 					case 4:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x2));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xD));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x2));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xD));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x2));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xD));
 						break;
 
 					//96KHZ
 					case 2:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xA));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x5));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xA));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x5));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xA));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x5));
 						break;
 
 					//192KHZ
 					case 1:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xE));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x1));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xE));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x1));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xE));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x1));
 						break;
 
 					default:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(1));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0));
 						break;
 				}
 			}else{  //22.5792MHz
@@ -338,59 +413,50 @@ static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int d
 				{
 					//22.05khz
 					case 8:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x4));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xb));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x4));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xb));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x4));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xb));
 						break;
 
 					//44.1KHZ
 					case 4:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x0));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xF));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x0));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0xF));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x0));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0xF));
 						break;
 
 					//88.2khz
 					case 2:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x8));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x7));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0x8));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x7));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0x8));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x7));
 						break;
 
 					//176.4KHZ
 					case 1:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xC));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x3));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(0xC));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0x3));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(0xC));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0x3));
 						break;
 
 					default:
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
-
-						reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
-						reg_val |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0));
-						writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+						reg_val_txchsta0 |= (SUNXI_SPDIF_TXCHSTA0_SAMFREQ(1));
+						reg_val_txchsta1 |= (SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(0));
+						reg_val_rxchsta0 |= (SUNXI_SPDIF_RXCHSTA0_SAMFREQ(1));
+						reg_val_rxchsta1 |= (SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(0));
+						
 						break;
 				}
 			}
+			writel(reg_val_txchsta0, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
+		  	writel(reg_val_txchsta1, sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+
+			writel(reg_val_rxchsta0, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0);
+		  	writel(reg_val_rxchsta1, sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1);
 		}
 		break;
 		case SUNXI_DIV_BCLK:
@@ -399,7 +465,6 @@ static int sunxi_spdif_set_clkdiv(struct snd_soc_dai *cpu_dai, int div_id, int d
 		default:
 			return -EINVAL;
 	}
-
 	return 0;
 }
 
@@ -422,10 +487,13 @@ static void spdifregsave(void)
 {
 	regsave[0] = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL);
 	regsave[1] = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCFG);
-	regsave[2] = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL) | (0x3<<16);
+	regsave[2] = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL) | (0x3<<16);	//clear TX, RX fifo
 	regsave[3] = readl(sunxi_spdif.regs + SUNXI_SPDIF_INT);
 	regsave[4] = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
 	regsave[5] = readl(sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+	regsave[6] = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCFG);
+	regsave[7] = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0);
+	regsave[8] = readl(sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1);	
 }
 
 static void spdifregrestore(void)
@@ -436,14 +504,18 @@ static void spdifregrestore(void)
 	writel(regsave[3], sunxi_spdif.regs + SUNXI_SPDIF_INT);
 	writel(regsave[4], sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA0);
 	writel(regsave[5], sunxi_spdif.regs + SUNXI_SPDIF_TXCHSTA1);
+	writel(regsave[6], sunxi_spdif.regs + SUNXI_SPDIF_RXCFG);
+	writel(regsave[7], sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA0);
+	writel(regsave[8], sunxi_spdif.regs + SUNXI_SPDIF_RXCHSTA1);
 }
 
 //#ifdef CONFIG_PM
 static int sunxi_spdif_suspend(struct snd_soc_dai *cpu_dai)
 {
 	u32 reg_val;
-	printk("[SPDIF]Enter %s\n", __func__);
+	printk("[SPDIF] Enter %s\n", __func__);
 
+	//global disable
 	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL);
 	reg_val &= ~SUNXI_SPDIF_CTL_GEN;
 	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL);
@@ -453,7 +525,9 @@ static int sunxi_spdif_suspend(struct snd_soc_dai *cpu_dai)
 	//disable the module clock
 	clk_disable(spdif_moduleclk);
 
+	//clear start?
 	clk_disable(spdif_apbclk);
+	//clear end?
 
 	printk("[SPDIF]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__);
 	printk("[SPDIF]SPECIAL CLK 0x01c200C0 = %#x, line= %d\n", *(volatile int*)0xF1C200C0, __LINE__);
@@ -464,9 +538,9 @@ static int sunxi_spdif_suspend(struct snd_soc_dai *cpu_dai)
 static int sunxi_spdif_resume(struct snd_soc_dai *cpu_dai)
 {
 	u32 reg_val;
-	printk("[SPDIF]Enter %s\n", __func__);
+	printk("[SPDIF] Enter %s\n", __func__);
 
-	//disable the module clock
+	//enable the module clock
 	clk_enable(spdif_apbclk);
 
 	//enable the module clock
@@ -474,11 +548,18 @@ static int sunxi_spdif_resume(struct snd_soc_dai *cpu_dai)
 
 	spdifregrestore();
 
+	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
+	reg_val &= ~SUNXI_SPDIF_FCTL_FIFOSRC;	//set TX FIFO source select as APB bus
+	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
+
 	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL);
+	//soft reset SPDIF
+	reg_val |= SUNXI_SPDIF_CTL_RESET;
+	//global enable
 	reg_val |= SUNXI_SPDIF_CTL_GEN;
 	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL);
 
-	//printk("[SPDIF]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008);
+	printk("[SPDIF]PLL2 0x01c20008 = %#x\n", *(volatile int*)0xF1C20008);
 	printk("[SPDIF]SPECIAL CLK 0x01c20068 = %#x, line= %d\n", *(volatile int*)0xF1C20068, __LINE__);
 	printk("[SPDIF]SPECIAL CLK 0x01c200C0 = %#x, line = %d\n", *(volatile int*)0xF1C200C0, __LINE__);
 
@@ -487,11 +568,11 @@ static int sunxi_spdif_resume(struct snd_soc_dai *cpu_dai)
 
 #define SUNXI_SPDIF_RATES (SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT)
 static struct snd_soc_dai_ops sunxi_spdif_dai_ops = {
-	.trigger 		= sunxi_spdif_trigger,
+	.trigger 	= sunxi_spdif_trigger,
 	.hw_params 	= sunxi_spdif_hw_params,
-	.set_fmt 		= sunxi_spdif_set_fmt,
-	.set_clkdiv = sunxi_spdif_set_clkdiv,
-	.set_sysclk = sunxi_spdif_set_sysclk,
+	.set_fmt 	= sunxi_spdif_set_fmt,
+	.set_clkdiv 	= sunxi_spdif_set_clkdiv,
+	.set_sysclk 	= sunxi_spdif_set_sysclk,
 };
 static struct snd_soc_dai_driver sunxi_spdif_dai = {
 	.probe 		= sunxi_spdif_dai_probe,
@@ -502,14 +583,14 @@ static struct snd_soc_dai_driver sunxi_spdif_dai = {
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = SUNXI_SPDIF_RATES,
-	.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.formats = SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,},
 	.capture = {
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = SUNXI_SPDIF_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
-	.symmetric_rates = 1,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,},
 	.ops = &sunxi_spdif_dai_ops,
+	.symmetric_rates = 1,
 };
 
 static int __devinit sunxi_spdif_dev_probe(struct platform_device *pdev)
@@ -521,46 +602,58 @@ static int __devinit sunxi_spdif_dev_probe(struct platform_device *pdev)
 	if(sunxi_spdif.regs == NULL)
 		return -ENXIO;
 
-		//spdif apbclk
-		spdif_apbclk = clk_get(NULL, "apb_spdif");
-		if(-1 == clk_enable(spdif_apbclk)){
-			printk("spdif_apbclk failed! line = %d\n", __LINE__);
-		}
+	//spdif apbclk
+	spdif_apbclk = clk_get(NULL, "apb_spdif");
+	if(-1 == clk_enable(spdif_apbclk)){
+		printk("spdif_apbclk failed! line = %d\n", __LINE__);
+	}
 
-		spdif_pllx8 = clk_get(NULL, "audio_pllx8");
+	spdif_pllx8 = clk_get(NULL, "audio_pllx8");
 
-		//spdif pll2clk
-		spdif_pll2clk = clk_get(NULL, "audio_pll");
+	//spdif pll2clk
+	spdif_pll2clk = clk_get(NULL, "audio_pll");
 
-		//spdif module clk
-		spdif_moduleclk = clk_get(NULL, "spdif");
+	//spdif module clk
+	spdif_moduleclk = clk_get(NULL, "spdif");
 
-		if(clk_set_parent(spdif_moduleclk, spdif_pll2clk)){
-			printk("try to set parent of spdif_moduleclk to spdif_pll2ck failed! line = %d\n",__LINE__);
-		}
+	if(clk_set_parent(spdif_moduleclk, spdif_pll2clk)){
+		printk("try to set parent of spdif_moduleclk to spdif_pll2ck failed! line = %d\n",__LINE__);
+	}
 
-		if(clk_set_rate(spdif_moduleclk, 24576000/8)){
-			printk("set spdif_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__);
-		}
+	if(clk_set_rate(spdif_moduleclk, 24576000/8)){
+		printk("set spdif_moduleclk clock freq to 24576000 failed! line = %d\n", __LINE__);
+	}
 
-		if(-1 == clk_enable(spdif_moduleclk)){
-			printk("open spdif_moduleclk failed! line = %d\n", __LINE__);
-		}
+	if(-1 == clk_enable(spdif_moduleclk)){
+		printk("open spdif_moduleclk failed! line = %d\n", __LINE__);
+	}
 
-		//global enbale
-		reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL);
-		reg_val |= SUNXI_SPDIF_CTL_GEN;
-		writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL);
+	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
+	reg_val &= ~SUNXI_SPDIF_FCTL_FIFOSRC;	//set TX FIFO source select as APB bus
+	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_FCTL);
+
+	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL);
+	//soft reset SPDIF
+	reg_val |= SUNXI_SPDIF_CTL_RESET;
+	//global enable
+	reg_val |= SUNXI_SPDIF_CTL_GEN;
+	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL);
 
-		ret = snd_soc_register_dai(&pdev->dev, &sunxi_spdif_dai);
+	ret = snd_soc_register_dai(&pdev->dev, &sunxi_spdif_dai);
 
-		iounmap(sunxi_spdif.ioregs);
+	iounmap(sunxi_spdif.ioregs);
 
 	return 0;
 }
 
 static int __devexit sunxi_spdif_dev_remove(struct platform_device *pdev)
 {
+	int reg_val = 0;
+	//global disable
+	reg_val = readl(sunxi_spdif.regs + SUNXI_SPDIF_CTL);
+	reg_val &= ~SUNXI_SPDIF_CTL_GEN;
+	writel(reg_val, sunxi_spdif.regs + SUNXI_SPDIF_CTL);
+
 	/* release the module clock */
 	clk_disable(spdif_moduleclk);
 
diff --git a/sound/soc/sunxi/spdif/sunxi_spdif.h b/sound/soc/sunxi/spdif/sunxi_spdif.h
index 67955d8..08630c9 100644
--- a/sound/soc/sunxi/spdif/sunxi_spdif.h
+++ b/sound/soc/sunxi/spdif/sunxi_spdif.h
@@ -17,127 +17,134 @@
 #define SUNXI_SPDIF_H_
 
 /*------------------SPDIF register definition--------------------*/
-#define SUNXI_SPDIFBASE 0x01C21000
+#define SUNXI_SPDIFBASE 				0x01C21000
 
-#define	SUNXI_SPDIF_CTL	(0x00)
+#define	SUNXI_SPDIF_CTL					(0x00)
 	#define SUNXI_SPDIF_CTL_MCLKDIV(v)		((v)<<4)		//v even
-	#define SUNXI_SPDIF_CTL_MCLKOUTEN			(1<<2)
-	#define SUNXI_SPDIF_CTL_GEN						(1<<1)
-	#define SUNXI_SPDIF_CTL_RESET					(1<<0)
+	#define SUNXI_SPDIF_CTL_MCLKOUTEN		(1<<2)
+	#define SUNXI_SPDIF_CTL_GEN			(1<<1)
+	#define SUNXI_SPDIF_CTL_RESET			(1<<0)
 
-#define SUNXI_SPDIF_TXCFG (0x04)
+#define SUNXI_SPDIF_TXCFG 				(0x04)
 	#define SUNXI_SPDIF_TXCFG_SINGLEMOD		(1<<31)
-	#define SUNXI_SPDIF_TXCFG_ASS					(1<<17)
+	#define SUNXI_SPDIF_TXCFG_ASS			(1<<17)
 	#define SUNXI_SPDIF_TXCFG_NONAUDIO		(1<<16)
-	#define SUNXI_SPDIF_TXCFG_TXRATIO(v)	((v)<<4)
-	#define SUNXI_SPDIF_TXCFG_FMTRVD			(3<<2)
+	#define SUNXI_SPDIF_TXCFG_TXRATIO(v)		((v)<<4)
+	#define SUNXI_SPDIF_TXCFG_FMTRVD		(3<<2)
 	#define SUNXI_SPDIF_TXCFG_FMT16BIT		(0<<2)
 	#define SUNXI_SPDIF_TXCFG_FMT20BIT		(1<<2)
 	#define SUNXI_SPDIF_TXCFG_FMT24BIT		(2<<2)
 	#define SUNXI_SPDIF_TXCFG_CHSTMODE		(1<<1)
-	#define SUNXI_SPDIF_TXCFG_TXEN				(1<<0)
+	#define SUNXI_SPDIF_TXCFG_TXEN			(1<<0)
 
 #define SUNXI_SPDIF_RXCFG (0x08)
 	#define SUNXI_SPDIF_RXCFG_LOCKFLAG		(1<<4)
-	#define SUNXI_SPDIF_RXCFG_CHSTSRC			(1<<3)
-	#define SUNXI_SPDIF_RXCFG_CHSTCP			(1<<1)
-	#define SUNXI_SPDIF_RXCFG_RXEN				(1<<0)
-
-#define SUNXI_SPDIF_TXFIFO (0x0C)
-
-#define SUNXI_SPDIF_RXFIFO (0x10)
-
-#define SUNXI_SPDIF_FCTL (0x14)
-	#define SUNXI_SPDIF_FCTL_FIFOSRC			(1<<31)
-	#define SUNXI_SPDIF_FCTL_FTX					(1<<17)
-	#define SUNXI_SPDIF_FCTL_FRX					(1<<16)
-	#define SUNXI_SPDIF_FCTL_TXTL(v)			((v)<<8)
-	#define SUNXI_SPDIF_FCTL_RXTL(v)			(((v))<<3)
-	#define SUNXI_SPDIF_FCTL_TXIM(v)			((v)<<2)
-	#define SUNXI_SPDIF_FCTL_RXOM(v)			((v)<<0)
-
-#define SUNXI_SPDIF_FSTA (0x18)
-	#define SUNXI_SPDIF_FSTA_TXE					(1<<14)
+	#define SUNXI_SPDIF_RXCFG_CHSTSRC		(1<<3)
+	#define SUNXI_SPDIF_RXCFG_CHSTCP		(1<<1)
+	#define SUNXI_SPDIF_RXCFG_RXEN			(1<<0)
+
+#define SUNXI_SPDIF_TXFIFO 				(0x0C)
+
+#define SUNXI_SPDIF_RXFIFO 				(0x10)
+
+#define SUNXI_SPDIF_FCTL 				(0x14)
+	#define SUNXI_SPDIF_FCTL_FIFOSRC		(1<<31)
+	#define SUNXI_SPDIF_FCTL_FTX			(1<<17)
+	#define SUNXI_SPDIF_FCTL_FRX			(1<<16)
+	#define SUNXI_SPDIF_FCTL_TXTL(v)		((v)<<8)
+	#define SUNXI_SPDIF_FCTL_RXTL(v)		((v)<<3)
+	#define SUNXI_SPDIF_FCTL_TXIM0			(0<<2)
+	#define SUNXI_SPDIF_FCTL_TXIM1			(1<<2)
+	#define SUNXI_SPDIF_FCTL_RXOM0			(0<<0)
+	#define SUNXI_SPDIF_FCTL_RXOM1			(1<<0)
+	#define SUNXI_SPDIF_FCTL_RXOM2			(2<<0)
+	#define SUNXI_SPDIF_FCTL_RXOM3			(3<<0)
+
+#define SUNXI_SPDIF_FSTA 				(0x18)
+	#define SUNXI_SPDIF_FSTA_TXE			(1<<14)
 	#define SUNXI_SPDIF_FSTA_TXECNTSHT		(8)
-	#define SUNXI_SPDIF_FSTA_RXA					(1<<6)
+	#define SUNXI_SPDIF_FSTA_RXA			(1<<6)
 	#define SUNXI_SPDIF_FSTA_RXACNTSHT		(0)
 
-#define SUNXI_SPDIF_INT (0x1C)
-	#define SUNXI_SPDIF_INT_RXLOCKEN			(1<<18)
+#define SUNXI_SPDIF_INT 				(0x1C)
+	#define SUNXI_SPDIF_INT_RXLOCKEN		(1<<18)
 	#define SUNXI_SPDIF_INT_RXUNLOCKEN		(1<<17)
 	#define SUNXI_SPDIF_INT_RXPARERREN		(1<<16)
-	#define SUNXI_SPDIF_INT_TXDRQEN				(1<<7)
-	#define SUNXI_SPDIF_INT_TXUIEN				(1<<6)
-	#define SUNXI_SPDIF_INT_TXOIEN				(1<<5)
-	#define SUNXI_SPDIF_INT_TXEIEN				(1<<4)
-	#define SUNXI_SPDIF_INT_RXDRQEN				(1<<2)
-	#define SUNXI_SPDIF_INT_RXOIEN				(1<<1)
-	#define SUNXI_SPDIF_INT_RXAIEN				(1<<0)
-
-#define SUNXI_SPDIF_ISTA (0x20)
+	#define SUNXI_SPDIF_INT_TXDRQEN			(1<<7)
+	#define SUNXI_SPDIF_INT_TXUIEN			(1<<6)
+	#define SUNXI_SPDIF_INT_TXOIEN			(1<<5)
+	#define SUNXI_SPDIF_INT_TXEIEN			(1<<4)
+	#define SUNXI_SPDIF_INT_RXDRQEN			(1<<2)
+	#define SUNXI_SPDIF_INT_RXOIEN			(1<<1)
+	#define SUNXI_SPDIF_INT_RXAIEN			(1<<0)
+
+#define SUNXI_SPDIF_ISTA 				(0x20)
 	#define SUNXI_SPDIF_ISTA_RXLOCKSTA		(1<<18)
-	#define SUNXI_SPDIF_ISTA_RXUNLOCKSTA	(1<<17)
-	#define SUNXI_SPDIF_ISTA_RXPARERRSTA	(1<<16)
-	#define SUNXI_SPDIF_ISTA_TXUSTA				(1<<6)
-	#define SUNXI_SPDIF_ISTA_TXOSTA				(1<<5)
-	#define SUNXI_SPDIF_ISTA_TXESTA				(1<<4)
-	#define SUNXI_SPDIF_ISTA_RXOSTA				(1<<1)
-	#define SUNXI_SPDIF_ISTA_RXASTA				(1<<0)
-
-#define SUNXI_SPDIF_TXCNT	(0x24)
-
-#define SUNXI_SPDIF_RXCNT	(0x28)
-
-#define SUNXI_SPDIF_TXCHSTA0 (0x2C)
-	#define SUNXI_SPDIF_TXCHSTA0_CLK(v)					((v)<<28)
-	#define SUNXI_SPDIF_TXCHSTA0_SAMFREQ(v)			((v)<<24)
-	#define SUNXI_SPDIF_TXCHSTA0_CHNUM(v)				((v)<<20)
-	#define SUNXI_SPDIF_TXCHSTA0_SRCNUM(v)			((v)<<16)
-	#define SUNXI_SPDIF_TXCHSTA0_CATACOD(v)			((v)<<8)
-	#define SUNXI_SPDIF_TXCHSTA0_MODE(v)				((v)<<6)
-	#define SUNXI_SPDIF_TXCHSTA0_EMPHASIS(v)	  ((v)<<3)
-	#define SUNXI_SPDIF_TXCHSTA0_CP							(1<<2)
-	#define SUNXI_SPDIF_TXCHSTA0_AUDIO					(1<<1)
-	#define SUNXI_SPDIF_TXCHSTA0_PRO						(1<<0)
-
-#define SUNXI_SPDIF_TXCHSTA1 (0x30)
-	#define SUNXI_SPDIF_TXCHSTA1_CGMSA(v)				((v)<<8)
+	#define SUNXI_SPDIF_ISTA_RXUNLOCKSTA		(1<<17)
+	#define SUNXI_SPDIF_ISTA_RXPARERRSTA		(1<<16)
+	#define SUNXI_SPDIF_ISTA_TXUSTA			(1<<6)
+	#define SUNXI_SPDIF_ISTA_TXOSTA			(1<<5)
+	#define SUNXI_SPDIF_ISTA_TXESTA			(1<<4)
+	#define SUNXI_SPDIF_ISTA_RXOSTA			(1<<1)
+	#define SUNXI_SPDIF_ISTA_RXASTA			(1<<0)
+	#define SUNXI_SPDIF_ISTA_RXCLR			(SUNXI_SPDIF_ISTA_RXLOCKSTA | SUNXI_SPDIF_ISTA_RXUNLOCKSTA | SUNXI_SPDIF_ISTA_RXPARERRSTA | SUNXI_SPDIF_ISTA_RXOSTA | SUNXI_SPDIF_ISTA_RXASTA)
+	#define SUNXI_SPDIF_ISTA_TXCLR			(SUNXI_SPDIF_ISTA_TXUSTA | SUNXI_SPDIF_ISTA_TXOSTA | SUNXI_SPDIF_ISTA_TXESTA)
+
+
+#define SUNXI_SPDIF_TXCNT				(0x24)
+
+#define SUNXI_SPDIF_RXCNT				(0x28)
+
+#define SUNXI_SPDIF_TXCHSTA0 				(0x2C)
+	#define SUNXI_SPDIF_TXCHSTA0_CLK(v)		((v)<<28)
+	#define SUNXI_SPDIF_TXCHSTA0_SAMFREQ(v)		((v)<<24)
+	#define SUNXI_SPDIF_TXCHSTA0_CHNUM(v)		((v)<<20)
+	#define SUNXI_SPDIF_TXCHSTA0_SRCNUM(v)		((v)<<16)
+	#define SUNXI_SPDIF_TXCHSTA0_CATACOD(v)		((v)<<8)
+	#define SUNXI_SPDIF_TXCHSTA0_MODE(v)		((v)<<6)
+	#define SUNXI_SPDIF_TXCHSTA0_EMPHASIS(v)	((v)<<3)
+	#define SUNXI_SPDIF_TXCHSTA0_CP			(1<<2)
+	#define SUNXI_SPDIF_TXCHSTA0_AUDIO		(1<<1)
+	#define SUNXI_SPDIF_TXCHSTA0_PRO		(1<<0)
+
+#define SUNXI_SPDIF_TXCHSTA1 				(0x30)
+	#define SUNXI_SPDIF_TXCHSTA1_CGMSA(v)		((v)<<8)
 	#define SUNXI_SPDIF_TXCHSTA1_ORISAMFREQ(v)	((v)<<4)
 	#define SUNXI_SPDIF_TXCHSTA1_SAMWORDLEN(v)	((v)<<1)
-	#define SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN			(1<<0)
-
-#define SUNXI_SPDIF_RXCHSTA0 (0x34)
-	#define SUNXI_SPDIF_RXCHSTA0_CLK(v)					((v)<<28)
-	#define SUNXI_SPDIF_RXCHSTA0_SAMFREQ(v)			((v)<<24)
-	#define SUNXI_SPDIF_RXCHSTA0_CHNUM(v)				((v)<<20)
-	#define SUNXI_SPDIF_RXCHSTA0_SRCNUM(v)			((v)<<16)
-	#define SUNXI_SPDIF_RXCHSTA0_CATACOD(v)			((v)<<8)
-	#define SUNXI_SPDIF_RXCHSTA0_MODE(v)				((v)<<6)
-	#define SUNXI_SPDIF_RXCHSTA0_EMPHASIS(v)	  ((v)<<3)
-	#define SUNXI_SPDIF_RXCHSTA0_CP							(1<<2)
-	#define SUNXI_SPDIF_RXCHSTA0_AUDIO					(1<<1)
-	#define SUNXI_SPDIF_RXCHSTA0_PRO						(1<<0)
-
-#define SUNXI_SPDIF_RXCHSTA1 (0x38)
-	#define SUNXI_SPDIF_RXCHSTA1_CGMSA(v)				((v)<<8)
+	#define SUNXI_SPDIF_TXCHSTA1_MAXWORDLEN		(1<<0)
+
+#define SUNXI_SPDIF_RXCHSTA0 				(0x34)
+	#define SUNXI_SPDIF_RXCHSTA0_CLK(v)		((v)<<28)
+	#define SUNXI_SPDIF_RXCHSTA0_SAMFREQ(v)		((v)<<24)
+	#define SUNXI_SPDIF_RXCHSTA0_CHNUM(v)		((v)<<20)
+	#define SUNXI_SPDIF_RXCHSTA0_SRCNUM(v)		((v)<<16)
+	#define SUNXI_SPDIF_RXCHSTA0_CATACOD(v)		((v)<<8)
+	#define SUNXI_SPDIF_RXCHSTA0_MODE(v)		((v)<<6)
+	#define SUNXI_SPDIF_RXCHSTA0_EMPHASIS(v)	((v)<<3)
+	#define SUNXI_SPDIF_RXCHSTA0_CP			(1<<2)
+	#define SUNXI_SPDIF_RXCHSTA0_AUDIO		(1<<1)
+	#define SUNXI_SPDIF_RXCHSTA0_PRO		(1<<0)
+
+#define SUNXI_SPDIF_RXCHSTA1 				(0x38)
+	#define SUNXI_SPDIF_RXCHSTA1_CGMSA(v)		((v)<<8)
 	#define SUNXI_SPDIF_RXCHSTA1_ORISAMFREQ(v)	((v)<<4)
 	#define SUNXI_SPDIF_RXCHSTA1_SAMWORDLEN(v)	((v)<<1)
-	#define SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN			(1<<0)
+	#define SUNXI_SPDIF_RXCHSTA1_MAXWORDLEN		(1<<0)
 
 /*--------------------------------CCM register definition---------------------*/
-#define SUNXI_CCMBASE (0x01C20000)
+#define SUNXI_CCMBASE 					(0x01C20000)
 
-#define SUNXI_CCMBASE_AUDIOHOSCPLL (0x08)
-	#define SUNXI_CCMBASE_AUDIOHOSCPLL_EN			(1<<31)
-	#define SUNXI_CCMBASE_AUDIOHOSCPLL_24576M		(1<<27)
-	#define SUNXI_CCMBASE_AUDIOHOSCPLL_225792M 		(0<<27)
+#define SUNXI_CCMBASE_AUDIOHOSCPLL 			(0x08)
+	#define SUNXI_CCMBASE_AUDIOHOSCPLL_EN		(1<<31)
+	#define SUNXI_CCMBASE_AUDIOHOSCPLL_24576M	(1<<27)
+	#define SUNXI_CCMBASE_AUDIOHOSCPLL_225792M 	(0<<27)
 
-#define SUNXI_CCMBASE_APBGATE	(0x68)
-	#define SUNXI_CCMBASE_APBGATE_SPDIFGATE	(1<<1)
+#define SUNXI_CCMBASE_APBGATE				(0x68)
+	#define SUNXI_CCMBASE_APBGATE_SPDIFGATE		(1<<1)
 
-#define SUNXI_CCMBASE_AUDIOCLK (0xC0)
+#define SUNXI_CCMBASE_AUDIOCLK 				(0xC0)
 	#define SUNXI_CCMBASE_AUDIOCLK_SPDIFSPEGATE	(1<<31)
-	#define SUNXI_CCMBASE_AUDIOCLK_DIV(v)			((v)<<16)
+	#define SUNXI_CCMBASE_AUDIOCLK_DIV(v)		((v)<<16)
 
 	/* Clock dividers */
 	#define SUNXI_DIV_MCLK	0
@@ -156,6 +163,6 @@ extern struct sunxi_spdif_info sunxi_spdif;
 unsigned int sunxi_spdif_get_clockrate(void);
 
 extern void sunxi_snd_txctrl(struct snd_pcm_substream *substream, int on);
-extern void sunxi_snd_rxctrl(int on);
+extern void sunxi_snd_rxctrl(struct snd_pcm_substream *substream, int on);
 
 #endif
diff --git a/sound/soc/sunxi/spdif/sunxi_spdma.c b/sound/soc/sunxi/spdif/sunxi_spdma.c
index ec4ac99..d5377fc 100644
--- a/sound/soc/sunxi/spdif/sunxi_spdma.c
+++ b/sound/soc/sunxi/spdif/sunxi_spdma.c
@@ -34,25 +34,40 @@
 #include "sunxi_spdif.h"
 #include "sunxi_spdma.h"
 
-static volatile unsigned int dmasrc = 0;
-static volatile unsigned int dmadst = 0;
+static const struct snd_pcm_hardware sunxi_pcm_out_hardware = {
+	.info			= SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				      SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+				      SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE |SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+	.rates			= SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT,
+	.rate_min		= 8000,
+	.rate_max		= 192000,
+	.channels_min		= 1,
+	.channels_max		= 2,
+	.buffer_bytes_max	= 128*1024,  /* value must be (2^n)Kbyte size */
+	.period_bytes_min	= 1024,//1024*4,
+	.period_bytes_max	= 1024*32,//1024*128,
+	.periods_min		= 4,
+	.periods_max		= 8,
+	.fifo_size		= 32,
+};
 
-static const struct snd_pcm_hardware sunxi_pcm_hardware = {
+static const struct snd_pcm_hardware sunxi_pcm_in_hardware = {
 	.info			= SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				      SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
 				      SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME,
-	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE |SNDRV_PCM_FMTBIT_S20_3LE| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
 	.rates			= SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT,
 	.rate_min		= 8000,
 	.rate_max		= 192000,
 	.channels_min		= 1,
 	.channels_max		= 2,
-	.buffer_bytes_max	= 128*1024,  //1024*1024  /* value must be (2^n)Kbyte size */
-	.period_bytes_min	= 1024*4,//1024*4,
+	.buffer_bytes_max	= 128*1024,  /* value must be (2^n)Kbyte size */
+	.period_bytes_min	= 1024,//1024*4,
 	.period_bytes_max	= 1024*32,//1024*128,
-	.periods_min		= 4,//8,
-	.periods_max		= 8,//8,
-	.fifo_size		= 32,//32,
+	.periods_min		= 4,
+	.periods_max		= 8,
+	.fifo_size		= 32,
 };
 
 struct sunxi_runtime_data {
@@ -65,6 +80,8 @@ struct sunxi_runtime_data {
 	dma_addr_t dma_pos;
 	dma_addr_t dma_end;
 	struct sunxi_dma_params *params;
+	/*DMA data width*/
+	unsigned int dma_width;
 };
 
 static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream)
@@ -73,8 +90,8 @@ static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream)
 	dma_addr_t pos = prtd->dma_pos;
 	unsigned int limit;
 	int ret;
-
 	unsigned long len = prtd->dma_period;
+	int read = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
 
   	limit = prtd->dma_limit;
   	while(prtd->dma_loaded < limit){
@@ -82,7 +99,7 @@ static void sunxi_pcm_enqueue(struct snd_pcm_substream *substream)
 			len  = prtd->dma_end - pos;
 		}
 
-		ret = sunxi_dma_enqueue(prtd->params, pos, len, 0);
+		ret = sunxi_dma_enqueue(prtd->params, pos, len, read);
 		if (ret == 0) {
 			prtd->dma_loaded++;
 			pos += prtd->dma_period;
@@ -120,17 +137,24 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream,
 	struct sunxi_runtime_data *prtd = runtime->private_data;
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	unsigned long totbytes = params_buffer_bytes(params);
-	struct sunxi_dma_params *dma =
-					snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+	struct sunxi_dma_params *dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 	int ret = 0;
 	if (!dma)
 		return 0;
 
+	if (SNDRV_PCM_FORMAT_S16_LE == params_format(params)) {
+		prtd->dma_width = 16;
+	}
+	else {
+		prtd->dma_width = 32;
+	}
+		
 	if (prtd->params == NULL) {
 		prtd->params = dma;
 		ret = sunxi_dma_request(prtd->params, 0);
 		if (ret < 0) {
-				return ret;
+			pr_err("[SPDIF] sunxi_dma_request failed! (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__);
+			return ret;
 		}
 	}
 
@@ -138,6 +162,7 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream,
 							    substream) != 0) {
 		sunxi_dma_release(prtd->params);
 		prtd->params = NULL;
+		pr_err("[SPDIF] sunxi_dma_set_callback failed! (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__);
 		return -EINVAL;
 	}
 
@@ -153,6 +178,7 @@ static int sunxi_pcm_hw_params(struct snd_pcm_substream *substream,
 	prtd->dma_pos = prtd->dma_start;
 	prtd->dma_end = prtd->dma_start + totbytes;
 	spin_unlock_irq(&prtd->lock);
+
 	return 0;
 }
 
@@ -197,16 +223,22 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream)
 #else
 		dma_config_t spdif_dma_conf;
 		memset(&spdif_dma_conf, 0, sizeof(spdif_dma_conf));
-		spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT;
+		if(prtd->dma_width > 16) {
+			spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT;
+			spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT;
+		}
+		else {
+			spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT;
+			spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT;
+		}
 		spdif_dma_conf.xfer_type.src_bst_len = DATA_BRST_4;
-		spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT;
 		spdif_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4;
 		spdif_dma_conf.address_type.src_addr_mode = NDMA_ADDR_INCREMENT;
 		spdif_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_NOCHANGE;
 		spdif_dma_conf.bconti_mode = false;
 		spdif_dma_conf.irq_spt = CHAN_IRQ_FD;
 		spdif_dma_conf.src_drq_type = N_SRC_SDRAM;
-		spdif_dma_conf.dst_drq_type = N_DST_SPDIF_TX;//DRQDST_SPDIFTX;
+		spdif_dma_conf.dst_drq_type = N_DST_SPDIF_TX;
 #endif
 		ret = sunxi_dma_config(prtd->params, &spdif_dma_conf, 0);
 	} else {
@@ -221,15 +253,32 @@ static int sunxi_pcm_prepare(struct snd_pcm_substream *substream)
 		spdif_dma_conf.hf_irq       = SW_DMA_IRQ_FULL|SW_DMA_IRQ_HALF;
 		spdif_dma_conf.from         = prtd->params->dma_addr;
 		spdif_dma_conf.to           = prtd->dma_start;
-		ret = sunxi_dma_config(prtd->params, &spdif_dma_conf, 0);
 #else
-		return -EINVAL;
+		dma_config_t spdif_dma_conf;
+		memset(&spdif_dma_conf, 0, sizeof(spdif_dma_conf));
+		if(prtd->dma_width > 16) {
+			spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_32BIT;
+			spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_32BIT;
+		}
+		else {
+			spdif_dma_conf.xfer_type.src_data_width = DATA_WIDTH_16BIT;
+			spdif_dma_conf.xfer_type.dst_data_width = DATA_WIDTH_16BIT;
+		}
+		spdif_dma_conf.xfer_type.src_bst_len = DATA_BRST_4;
+		spdif_dma_conf.xfer_type.dst_bst_len = DATA_BRST_4;
+		spdif_dma_conf.address_type.src_addr_mode = NDMA_ADDR_NOCHANGE;
+		spdif_dma_conf.address_type.dst_addr_mode = NDMA_ADDR_INCREMENT;
+		spdif_dma_conf.bconti_mode = false;
+		spdif_dma_conf.irq_spt = CHAN_IRQ_FD;
+		spdif_dma_conf.src_drq_type = N_SRC_SPDIF_RX;
+		spdif_dma_conf.dst_drq_type = N_DST_SDRAM;
 #endif
+		ret = sunxi_dma_config(prtd->params, &spdif_dma_conf, 0);
 	}
 	/* flush the DMA channel */
 	prtd->dma_loaded = 0;
-	if (sunxi_dma_flush(prtd->params) == 0)
-		prtd->dma_pos = prtd->dma_start;
+	sunxi_dma_flush(prtd->params);
+	prtd->dma_pos = prtd->dma_start;
 
 	/* enqueue dma buffers */
 	sunxi_pcm_enqueue(substream);
@@ -248,12 +297,14 @@ static int sunxi_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		sunxi_dma_start(prtd->params);
+		prtd->state |= ST_RUNNING;
 		break;
 
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		sunxi_dma_stop(prtd->params);
+		prtd->state &= ~ST_RUNNING;
 		break;
 
 	default:
@@ -271,21 +322,26 @@ static snd_pcm_uframes_t sunxi_pcm_pointer(struct snd_pcm_substream *substream)
 	struct sunxi_runtime_data *prtd = runtime->private_data;
 	unsigned long res = 0;
 	snd_pcm_uframes_t offset = 0;
+	dma_addr_t dmasrc = 0;
+	dma_addr_t dmadst = 0;
+
 
 	spin_lock(&prtd->lock);
 	sunxi_dma_getcurposition(prtd->params,
 				 (dma_addr_t*)&dmasrc, (dma_addr_t*)&dmadst);
 
-	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE){
-		res = dmadst - prtd->dma_start;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK){
+		res = dmasrc + prtd->dma_period - prtd->dma_start;
 	} else {
-		offset = bytes_to_frames(runtime, dmasrc + prtd->dma_period - runtime->dma_addr);
+	    	res = dmadst + prtd->dma_period - prtd->dma_start;
 	}
+	offset = bytes_to_frames(runtime, res);
 	spin_unlock(&prtd->lock);
 
 	if(offset >= runtime->buffer_size)
 		offset = 0;
-		return offset;
+
+	return offset;
 }
 
 static int sunxi_pcm_open(struct snd_pcm_substream *substream)
@@ -294,11 +350,17 @@ static int sunxi_pcm_open(struct snd_pcm_substream *substream)
 	struct sunxi_runtime_data *prtd;
 
 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-	snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_hardware);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_out_hardware);
+	} else {
+		snd_soc_set_runtime_hwparams(substream, &sunxi_pcm_in_hardware);
+	}	
 
 	prtd = kzalloc(sizeof(struct sunxi_runtime_data), GFP_KERNEL);
-	if (prtd == NULL)
+	if (prtd == NULL) {
+		pr_err("[SPDIF] try to alloc sunxi_runtime_data failed %s,%d\n", __func__,__LINE__);
 		return -ENOMEM;
+	}
 
 	spin_lock_init(&prtd->lock);
 
@@ -311,7 +373,6 @@ static int sunxi_pcm_close(struct snd_pcm_substream *substream)
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct sunxi_runtime_data *prtd = runtime->private_data;
 	kfree(prtd);
-
 	return 0;
 }
 
@@ -342,8 +403,13 @@ static int sunxi_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
 {
 	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
 	struct snd_dma_buffer *buf = &substream->dma_buffer;
-	size_t size = sunxi_pcm_hardware.buffer_bytes_max;
+	size_t size = 0;
 
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		size = sunxi_pcm_out_hardware.buffer_bytes_max;
+	} else {
+		size = sunxi_pcm_in_hardware.buffer_bytes_max;
+	}	
 	buf->dev.type = SNDRV_DMA_TYPE_DEV;
 	buf->dev.dev = pcm->card->dev;
 	buf->private_data = NULL;
@@ -384,6 +450,7 @@ static int sunxi_pcm_new(struct snd_soc_pcm_runtime *rtd)
 	struct snd_pcm *pcm = rtd->pcm;
 	int ret = 0;
 
+
 	if (!card->dev->dma_mask)
 		card->dev->dma_mask = &sunxi_pcm_mask;
 	if (!card->dev->coherent_dma_mask)
@@ -443,16 +510,19 @@ static int __init sunxi_soc_platform_spdif_init(void)
 	int ret, spdif_used = 0;
 
 	ret = script_parser_fetch("spdif_para", "spdif_used", &spdif_used, 1);
-	if (ret != 0 || !spdif_used)
+	if (ret != 0 || !spdif_used) 
 		return -ENODEV;
 
 	ret = platform_device_register(&sunxi_spdif_pcm_device);
-	if (ret < 0)
+	if (ret < 0) {
+		pr_err("[SPDIF] try to SPDIF platform_device_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__);
 		return ret;
+	}
 
 	ret = platform_driver_register(&sunxi_spdif_pcm_driver);
 	if (ret < 0) {
 		platform_device_unregister(&sunxi_spdif_pcm_device);
+		pr_err("[SPDIF] try to SPDIF platform_driver_register failed (ret=(%d)) failed %s,%d\n", ret, __func__,__LINE__);
 		return ret;
 	}
 	return 0;
diff --git a/sound/soc/sunxi/sunxi-codec.c b/sound/soc/sunxi/sunxi-codec.c
index 1b7f8f1..197fa84 100644
--- a/sound/soc/sunxi/sunxi-codec.c
+++ b/sound/soc/sunxi/sunxi-codec.c
@@ -1155,8 +1155,8 @@ static int snd_sunxi_codec_prepare(struct	snd_pcm_substream	*substream)
 	  					&codec_play_dma_conf, 0);
 	  	/* flush the DMA channel */
 		play_prtd->dma_loaded = 0;
-		if (sunxi_dma_flush(play_prtd->params) == 0)
-			play_prtd->dma_pos = play_prtd->dma_start;
+		sunxi_dma_flush(play_prtd->params);
+		play_prtd->dma_pos = play_prtd->dma_start;
 		/* enqueue dma buffers */
 		sunxi_pcm_enqueue(substream);
 		return play_ret;
@@ -1196,8 +1196,8 @@ static int snd_sunxi_codec_prepare(struct	snd_pcm_substream	*substream)
 	  					&codec_capture_dma_conf, 0);
 	  	/* flush the DMA channel */
 		capture_prtd->dma_loaded = 0;
-		if (sunxi_dma_flush(capture_prtd->params) == 0)
-			capture_prtd->dma_pos = capture_prtd->dma_start;
+		sunxi_dma_flush(capture_prtd->params);
+		capture_prtd->dma_pos = capture_prtd->dma_start;
 
 		/* enqueue dma buffers */
 		sunxi_pcm_enqueue(substream);
